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.

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
afterInteractivealone 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
| Strategy | Performance Impact | Tracking Reliability | Complexity | Recommended For |
|---|---|---|---|---|
| After Interactive (Recommended Default) | Good | High (manual SPA page views required) | Low | Most Next.js apps; balanced performance & analytics |
| Before Interactive (Early Load) | Moderate | Very High | Low | Marketing-heavy pages; critical attribution tracking |
| Lazy Onload (After Page Fully Loads) | Very Good | Medium | Low | Content-focused or SEO-driven pages; analytics not critical |
| Interaction-Based Load | Excellent | Low | Medium | Blogs or informational pages; extreme performance priority |
| Interaction with Event Buffering | Excellent | High | Medium-High | Performance-first sites needing reliable early tracking |
| Conditional Load | Good | Medium | Medium-High | GDPR-heavy, multi-region, or multi-brand setups |
| Server-Side GTM | Good | Very High | High | High-traffic apps; long-term analytics & reliability |
| Web Worker (Partytown) | Excellent | High | High | Performance-critical sites; heavy third-party scripts |
