Skip to main content

Google Tag Manager Loading Strategies in Next.js

This article explains the main GTM loading strategies in Next.js, how each one affects performance and event tracking, and when each approach makes sense.

Google Tag Manager Loading Strategies in Next.js

Performance, Analytics Accuracy, and Risk-Aware Implementation

Google Tag Manager (GTM) can significantly affect Next.js apps. Choosing the right loading strategy requires balancing performance, tracking reliability, and implementation risk.

This guide covers seven strategies, with performance / analytics / complexity & risk explained for each, including common mistakes and implementation examples.

Why GTM Loading Matters in Next.js

Next.js applications differ from traditional websites in important ways:

  • Pages are server-rendered and then hydrated on the client
  • JavaScript execution during hydration affects interaction metrics (INP)
  • Client-side navigation does not reload the page
  • Page views must be tracked manually on route changes

Because of this, GTM loading strategy directly affects:

  • Core Web Vitals (especially INP and LCP)
  • Hydration speed and responsiveness
  • Analytics accuracy and attribution

1. Balanced Approach – After-Interactive Loading (Recommended Default)

Performance: Good
Analytics: High
Complexity / Risk: Low

What this means

  • GTM loads after the page becomes interactive using strategy="afterInteractive".
  • Hydration is not blocked, but GTM may execute while hydration is still finishing.

Common mistakes

  • Forgetting SPA page view tracking
  • Assuming afterInteractive alone guarantees good INP
  • Leaving GTM’s default page view trigger enabled and causing duplicates
import Script from "next/script";

const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID!;

export function GTM() {
return (
<Script
id="gtm"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
// Ensure dataLayer exists before GTM loads
window.dataLayer = window.dataLayer || [];

(function(w,d,s,l,i){
w[l]=w[l]||[];
w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});
var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),
dl=l!='dataLayer'?'&l='+l:'';
j.async=true;
j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${GTM_ID}');
`,
}}
/>
);
}

SPA page view tracking — required Because Next.js uses client-side routing, page views must be sent manually on route changes.

Note: If you do this, disable GTM’s default Page View trigger to avoid duplicates.

// SPA page view tracking — Pages Router
import { useRouter } from "next/router";
import { useEffect } from "react";

export function usePageViews() {
const router = useRouter();

useEffect(() => {
const handleRouteChange = (url: string) => {
window.dataLayer?.push({
event: "page_view",
page_path: url,
page_location: window.location.href,
});
};

router.events.on("routeChangeComplete", handleRouteChange);
return () =>
router.events.off("routeChangeComplete", handleRouteChange);
}, [router]);
}
// SPA page view tracking — App Router
"use client";

import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";

export function PageViewTracker() {
const pathname = usePathname();
const searchParams = useSearchParams();

useEffect(() => {
const pagePath =
pathname + (searchParams.toString() ? `?${searchParams.toString()}` : "");

window.dataLayer?.push({
event: "page_view",
page_path: pagePath,
page_location: window.location.href,
});
}, [pathname, searchParams]);

return null;
}

2. Max Tracking – Early Loading (Before-Interactive)

  • Performance: Moderate / Risky
  • Analytics: Maximum
  • Complexity / Risk: Low

What this means

  • GTM loads before React hydration starts using beforeInteractive.
<Script strategy="beforeInteractive" src={`https://www.googletagmanager.com/gtm.js?id=${GTM_ID}`}/>

Common mistakes

  • INP regressions caused by long GTM tasks
  • Duplicate page views when default GTM page view triggers are combined with SPA tracking
  • Assuming “early load” only affects LCP (INP is usually the real casualty)
  • Letting marketing add heavy tags without performance review

When to use

  • Marketing-heavy pages
  • Conversion funnels where attribution accuracy is critical

3. Performance-First – Lazy Loading (After Page Fully Loads)

  • Performance: Very Good
  • Analytics: Medium
  • Complexity / Risk: Low

What this means

  • GTM loads only after the browser load event.
<Script id="gtm" strategy="lazyOnload" src={`https://www.googletagmanager.com/gtm.js?id=${GTM_ID}`}/>

Common mistakes

  • Assuming Lighthouse ignores GTM entirely
  • Missing page views from short sessions

When to use

  • Content-heavy sites
  • SEO-driven pages where analytics is helpful but not critical

4. Extreme Performance – Interaction-Based Loading with Event Buffering

  • Performance: Excellent
  • Analytics: Medium–High
  • Complexity / Risk: Medium

What this means

  • GTM loads only after user interaction. Important events are buffered until GTM is ready.

Common mistakes

  • Users who never interact are not tracked
  • Event buffer flushed too late or never
  • Overengineering simple setups
// Example buffering logic
// Initialize dataLayer early
window.dataLayer = window.dataLayer || [];

const bufferedEvents: any[] = [];

// Track events before GTM is ready
export function trackEvent(event: any) {
if (!window.gtmReady) {
bufferedEvents.push(event);
} else {
window.dataLayer.push(event);
}
}

// Call this when GTM finishes loading
export function onGtmReady() {
bufferedEvents.forEach(event => window.dataLayer.push(event));
window.gtmReady = true;
}

5. Conditional Loading – Consent, Region, or Route-Based

  • Performance: Good
  • Analytics: Medium
  • Complexity / Risk: Medium–High

What this means

GTM is loaded only if certain conditions are met (for example, user consent).

{hasConsent && <GTM />}

Common mistakes

  • Inconsistent analytics across regions
  • Consent state not persisted correctly
  • Forgetting to update rules for new routes

When to use

  • GDPR-heavy environments
  • Multi-region or multi-brand sites

6. Server-Side GTM – High Reliability, High Complexity

  • Performance: Good
  • Analytics: Very High
  • Complexity / Risk: High

What this means

  • The browser sends events to your server, which forwards them to GTM and analytics tools.

Common mistakes

  • Poor client/server event parity
  • Added latency without batching
  • Underestimating maintenance cost

When to use

  • High-traffic applications
  • Long-term analytics infrastructure
  • The user's browser extensions are blocking analytics

7. Web Worker GTM (Partytown) – Maximum Frontend Performance

  • Performance: Excellent
  • Analytics: High
  • Complexity / Risk: High

What this means

  • GTM runs in a Web Worker using Partytown, keeping heavy scripts off the main thread.

Common mistakes

  • DOM-dependent tags silently failing
  • Missing forwarded APIs
  • Difficult debugging
import { Partytown } from "@builder.io/partytown/react";
import Script from "next/script";

<Partytown forward={["dataLayer.push"]} />

<Script
type="text/partytown"
src={`https://www.googletagmanager.com/gtm.js?id=${GTM_ID}`}
/>

or just

<Script src={`https://www.googletagmanager.com/gtm.js?id=${GTM_ID}`} strategy="worker" />

Final Recommendations

  • Default choice: After-Interactive loading + manual SPA page views
  • Maximum analytics reliability: Early loading or Server-Side GTM
  • Extreme performance: Interaction-based loading with buffering or Partytown
  • Regularly audit GTM tags — loading strategy alone won’t fix heavy scripts
  • Always validate SPA tracking when using the App Router
StrategyPerformance ImpactTracking ReliabilityComplexityRecommended For
After Interactive (Recommended Default)GoodHigh (manual SPA page views required)LowMost Next.js apps; balanced performance & analytics
Before Interactive (Early Load)ModerateVery HighLowMarketing-heavy pages; critical attribution tracking
Lazy Onload (After Page Fully Loads)Very GoodMediumLowContent-focused or SEO-driven pages; analytics not critical
Interaction-Based LoadExcellentLowMediumBlogs or informational pages; extreme performance priority
Interaction with Event BufferingExcellentHighMedium-HighPerformance-first sites needing reliable early tracking
Conditional LoadGoodMediumMedium-HighGDPR-heavy, multi-region, or multi-brand setups
Server-Side GTMGoodVery HighHighHigh-traffic apps; long-term analytics & reliability
Web Worker (Partytown)ExcellentHighHighPerformance-critical sites; heavy third-party scripts