Optimize Google Analytics with Tag Manager in Next.js 15
Master Google Analytics integration with Next.js 15.5.5 and Google Tag Manager to ensure GDPR compliance and avoid…

⚡ Next.js Implementation Guides
In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
If you're building with Next.js 15.5.5 and already rolled out the GDPR-friendly consent layer I described in Build a Cookie Consent Banner in Next.js 15, the next hurdle is Google analytics. Doubling up the Google Analytics snippet and Google Tag Manager is the quickest way to blow up your reports. After wiring this stack on our own site, I settled on a clean approach: let Google Tag Manager be the only script the app loads, then connect the GA4 property inside GTM.
The existing consent gate is the reason this works. As soon as the visitor grants analytics storage, we inject Google Tag Manager once, and the container decides which destinations to ping. That keeps the app compliant (just like in GDPR-Compliant Vercel Analytics) while preserving every hit the GA4 property already captured.
First, strip the direct GA snippet from your App Router layout and give GTM complete ownership. In my case the consent-aware loader lives in src/app/layout.tsx.
// File: src/app/layout.tsx
<GA4Consent gtmId="GTM-PSB7763V" />
The component behind that call sits at src/components/analytics/ga4-consent.tsx. It waits for our cookie banner to flip consent to true, then injects the standard Google Tag Manager bootstrap snippet plus the <noscript> iframe. Because we no longer pass a gaId, the direct gtag.js loader never runs, and duplicate page views disappear. The consent handler also pushes a default_consent_granted event into dataLayer, so GTM reads analytics storage as granted without extra plumbing.
With the app ready, move into Google Tag Manager and wire GA4 to the container. Open https://tagmanager.google.com, select the workspace for GTM-PSB7763V, and add a new tag. Rename it to something obvious like “Google Tag – GA4,” choose the Google Tag type, and paste your existing measurement ID G-RKCKVVHE9N. Leave the advanced configuration alone unless you have a reason to tweak data layer variables, then add the default “All Pages” trigger. Hit Preview, feed in your staging URL, and GTM’s debug overlay will show the consent event followed by the Google Tag firing exactly once.
When everything looks right, click Submit → Publish so the live container matches your workspace. At this point Google Tag Manager is bootstrapping gtag.js for you, sending page views to the same GA4 property you relied on before, and keeping the history intact because the measurement ID never changed. Fire up the revived Tag Assistant extension and you’ll see the container GTM-PSB7763V, the Google Tag ID GT-WBKDQQKB, and the GA4 property G-RKCKVVHE9N all tied together. A quick glance at GA Realtime confirms you’re getting single hits per page view.
By letting Google Tag Manager be the only analytics script in Next.js 15.5.5, we stay compliant, avoid duplicate signals, and keep the GA4 property untouched. You now have a single place to manage future tags or conversions without revisiting the codebase. Let me know in the comments if you have questions, and subscribe for more practical development guides. Thanks, Matija