Enquiry forms that land in the CMS and the CRM
How we build enquiry forms that verify with reCAPTCHA, store in Payload CMS, and sync to HubSpot, one server action, no client-side secrets.
The Blog
Marketing needs tracking tags. Performance needs discipline. We load Google Tag Manager through Next.js's own third-party helpers so analytics doesn't compete with the content visitors came to see.
The problem
Every marketing site needs analytics. Most need Google Tag Manager for event tracking, conversion pixels, and remarketing tags. The tension is that every third-party script competes with your own content for browser resources, network bandwidth, parse time, execution time.
The classic approach is pasting a script snippet in the <head>. It works, but the browser has to download, parse, and execute the script before it can finish rendering the page. On a fast connection, the impact is small. On a mobile device over 4G, which is how most visitors actually arrive, a render-blocking script in the head can measurably delay LCP.
The problem compounds. GTM loads its own container, which loads additional scripts based on the tags configured inside it. One snippet in the head becomes a cascade of requests, each adding latency before the visitor sees the content they came for.
The approach
Next.js provides @next/third-parties a package with components that load common third-party services using optimised strategies. Instead of a raw script tag, you render a <GoogleTagManager> component with your container ID. The component handles loading timing, script placement, and the data layer setup.
In our layout, we load GTM if a container ID is configured. If there's no GTM ID but there is a Google Analytics measurement ID, we fall back to the <GoogleAnalytics> component instead. This keeps the layout clean, one conditional, not two competing script blocks.
The key difference from a raw snippet is loading strategy. The @next/third-parties components defer script loading so they don't block initial render. The browser prioritises your content, HTML, CSS, above-the-fold images, and the analytics script loads after the critical path is clear.
Honest limits
Loading GTM through `@next/third-parties` reduces the impact on initial render, but it doesn't eliminate the cost of third-party scripts entirely. GTM still downloads its container. The tags inside that container, analytics, remarketing pixels, conversion tracking, still execute and make their own network requests.
The real discipline is in what goes inside GTM, not just how GTM loads. Every tag added to the container has a performance cost. Consent management platforms, heat mapping tools, chat widgets, A/B testing scripts, each one adds JavaScript that the browser has to process.
We keep the GTM container lean. Essential analytics and conversion tracking go in. Anything that can be deferred or loaded conditionally based on user interaction stays out of the initial container load.
The framework-level loading strategy handles the "when." The container management handles the "what." Both matter for Core Web Vitals
The choice
Google Tag Manager and Google Analytics can both be loaded independently. Running both creates redundant requests — GTM typically loads GA as one of its tags anyway.
We set the layout to check for a GTM container ID first. If it exists, we load GTM and let it manage GA internally. If there's no GTM container but there is a GA measurement ID, we load GA directly. This ensures exactly one analytics loading path per page, not two.
For most marketing sites, GTM is the better default. It gives the marketing team the ability to add and modify tags without code changes. GA alone requires developer involvement for every new tracking event or conversion pixel. The tradeoff is a slightly larger initial payload, GTM's container script plus its configuration, but the operational flexibility usually justifies it.