Google Tag Manager (GTM) lets marketing add pixels and events without redeploying the whole app for every change. This tutorial installs GTM cleanly in a Next.js App Router site.
What you will build
- A GTM container loaded from `layout.tsx`
- Environment variable for `GTM-XXXX`
- Noscript iframe fallback after `<body>`
- A guard so GTM only loads in production
Prerequisites
- A GTM account and web container ID (`GTM-XXXXXXX`)
- Next.js App Router
- Permission to edit `app/layout.tsx`
Step-by-step
- Create a GTM web container and copy the container ID.
- Add `NEXT_PUBLIC_GTM_ID=GTM-XXXXXXX` to production env (leave empty in local `.env`).
- Create `components/analytics/gtm.tsx` that renders the official script via `next/script`.
- Mount `<GoogleTagManager />` in the root layout `<head>` (or as early as Next allows).
- Add the noscript iframe immediately after `<body>`.
- Publish a Version in GTM and verify with Tag Assistant / Preview mode.
GTM component
import Script from "next/script";
const gtmId = process.env.NEXT_PUBLIC_GTM_ID;
export function GoogleTagManager() {
if (!gtmId) return null;
return (
<Script id="gtm" strategy="afterInteractive">{`
(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','${gtmId}');
`}</Script>
);
}
export function GoogleTagManagerNoscript() {
if (!gtmId) return null;
return (
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${gtmId}`}
height="0"
width="0"
style={{ display: "none", visibility: "hidden" }}
title="Google Tag Manager"
/>
</noscript>
);
}Layout wiring
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<GoogleTagManager />
</head>
<body>
<GoogleTagManagerNoscript />
{children}
</body>
</html>
);
}Verify
- Open the site in GTM Preview (Tag Assistant).
- Confirm Container Loaded fires on homepage and `/contact`.
- Check Network tab for `gtm.js?id=GTM-`.
Tips
- Never hard-code the container ID in multiple files
- Keep GA4, Ads, and LinkedIn Insight inside GTM, not as separate hard-coded scripts
- Document which tags are live so engineering knows what marketing owns
Next steps
Push a `page_view` dataLayer event on client navigations if you use a SPA-heavy App Router pattern, then follow our GTM custom events tutorial for forms and CTAs.


