How to add Google Tag Manager to a NextJS website
Asked Answered
R

11

23

From this answer and this article I've added a <Script> in order to add Google Tag Manager to my NextJS website:

components/layout.tsx:

 import React from "react";
 import Head from "next/head";
 import Script from 'next/script'

 <!-- skip some code -->

 <Head>
    <link rel="preconnect" href="https://cdn.sanity.io/" />
    <link rel="dns-prefetch" href="https://cdn.sanity.io//" />
  </Head>
  <Script id="google-tag-manager" 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=%27+i+dl;f.parentNode.insertBefore(j,f);
      })(window,document,'script','dataLayer','GTM-xxxx');
    }
  </Script>

However, the script is not present on the front end.

components/layout.tsx is the only file on my website with a <Head> or <head>.

The Google Tag Manager <noscript> is present on the front end, used in app/layout.tsx:

 <body className="antialiased text-gray-800 dark:bg-black dark:text-gray-400">
    <noscript
      dangerouslySetInnerHTML={{
        __html: <iframe src="https://www.googletagmanager.com/ns.html?id=xxxx" height="0" width="0" style="display: none; visibility: hidden;" />,
      }}
    />

so I know I've saved all changes.

Help appreciated.

Update

I am new to NextJS, and all of this code is from a NextJS/Sanity template, which works locally and on a Vercel staging site.

My only problem is not being able to load Google Tag Manager properly before publishing to my domain.

There is no _app.js in the project.

There is /app/(website)/layout.tsx:

import "@/styles/tailwind.css";
import { Providers } from "./providers";
import { cx } from "@/utils/all";
import { Inter, Lora } from "next/font/google";

const inter = Inter({
  subsets: ["latin"],
  variable: "--font-inter"
});

const lora = Lora({
  subsets: ["latin"],
  variable: "--font-lora"
});

export default function RootLayout({
  children
}: {
  children: React.ReactNode;
}) {
  return (
    <html
      lang="en"
      suppressHydrationWarning
      className={cx(inter.variable, lora.variable)}>
       <body className="antialiased text-gray-800 dark:bg-black dark:text-gray-400" >
        <noscript
          dangerouslySetInnerHTML={{
            __html: `<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-xxxx" height="0" width="0" style="display: none; visibility: hidden;" />`,
          }}
        />
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

This is /app/(website)/providers.tsx:

"use client";

import { ThemeProvider } from "next-themes";

    export function Providers({ children }) {
      return (
        <ThemeProvider attribute="class" defaultTheme="light">
          {children}
        </ThemeProvider>
      );
    }

If I search VSCode for RootLayout, nothing calls it, so I am a bit confused about which is the main file.

If you'd like access to the Github repo, I can provide it.

Rubberize answered 22/4, 2023 at 3:5 Comment(1)
You should check this out: github.com/XD2Sketch/next-google-tag-managerDelwyn
N
36

Check out new NextJs Third-Parties Component

After watching the NextJS Conf2023, they released @next/third-parties because of people reporting in the past months after they followed the Google Analytics Documentation, it made bad perfomance issues on Lighhouse.

After following NextJs documentation guide, I was able to fully setup Google Analytics on NextJs 14.

1. To load Google Tag Manager for all routes, include the component directly in your root layout:

app/layout.tsx

import { GoogleTagManager } from '@next/third-parties/google'

export default function RootLayout({children}: {children: React.ReactNode}) {
    return (
        <html lang="en">
            <body>{children}</body>
            <GoogleTagManager gtmId="GTM-XYZ" />
        </html>
     )
}

2. To load Google Tag Manager for a single route, include the component in your page file:

app/page.js

import { GoogleTagManager } from '@next/third-parties/google'

export default function Page() {
    return <GoogleTagManager gtmId="GTM-XYZ" />
}

Note: This is the Official NextJs Documentation.

Noahnoak answered 31/10, 2023 at 7:0 Comment(5)
Tried it on a NextJS 14 project and got import errors, This github issue showcaes the issue (it was automatically closed but still happening): github.com/vercel/next.js/issues/58458Grease
@DanielAviv I also encountered the import error - there's another open issue with additional info here (unresolved at time of writing): github.com/vercel/next.js/issues/58697Kaneshakang
It worked for me on one project but not another. But then github.com/vercel/next.js/issues/58697#issuecomment-1830677388 helped that one.Comeaux
For those getting errors I was going to mention to check your TypeScript configuration but @Ryan's comment has it covered.Philoprogenitive
I'm seeing undefined on gtm.js?id=undefined in the page source regardless of whatever value I'll pass to the GoogleTagManager prop gtmId.Practiced
D
10

In your _document.js file which is located in /pages directory add below code. Also if there is no any _document.js file then please create it.

Now add the functionality of Google Tag Manager as :

import Document, { Html, Head, Main, NextScript } from "next/document";

function MyDocument() {
  return (
    <Html>
      <Head>
        <script
          dangerouslySetInnerHTML={{
            __html: `
              (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=%27+i+dl;f.parentNode.insertBefore(j,f);
              })(window,document,'script','dataLayer','GOOGLE_TAG_MANAGER_ID');
            `,
          }}
        />
      </Head>
      <body>
        <noscript
          dangerouslySetInnerHTML={{
            __html: `
              <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-xxxx"
              height="0" width="0" style="display:none;visibility:hidden"></iframe>
            `,
          }}
        />
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

export default MyDocument;

Now you can check the functionality is working or not and please let me know if it not works. I will surely help.

Dayflower answered 14/5, 2023 at 11:15 Comment(2)
The NextJS/Sanity template didn't include _document.js and I don't want to complicate or break things by creating it. Yilmaz' answer solved the problem for me.Rubberize
I'm using a legacy Next JS (version 10) and this solution worked perfectly for me.Airtight
B
6

Answer from how-to-setup-google-tag-manager-in-a-next-13-app-router

// create a client component
"use client"
// you are using id=GTM-xxxx. no need for gtmId
import { gtmId, pageview } from "lib/gtm"
import { usePathname, useSearchParams } from "next/navigation"
import Script from "next/script"
import { useEffect } from "react"

export default function Analytics() {
  const pathname = usePathname()
  const searchParams = useSearchParams()

  useEffect(() => {
    if (pathname) {
      pageview(pathname)
    }
  }, [pathname, searchParams])

  if (process.env.NEXT_PUBLIC_VERCEL_ENV !== "production") {
    return null
  }

  return (
    <>
      <noscript>
        <iframe
          src={`https://www.googletagmanager.com/ns.html?id=GTM-xxxx`}
          height="0"
          width="0"
          style={{ display: "none", visibility: "hidden" }}
        />
      </noscript>
      <Script
        id="gtm-script"
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: `
    (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-xxxx");
  `,
        }}
      />
    </>
  )
}

import this in your RootLayout component

Barrator answered 15/5, 2023 at 1:43 Comment(1)
Replacing gtmId with GTM_ID in the code above and using this gtm.js workedRubberize
C
3

A simple way to add a Google Tag in Nextjs 13+ when using the app folder (instead of the pages folder):

Create a new component anywhere in your codebase for storing the Google tag script (for example components/GoogleAnalytics.tsx).

import Script from "next/script";

export const GoogleAnalyticsTracking = () => {
  return (
    <>
      {/* Global site tag (gtag.js) - Google Analytics */}
      <Script src="https://www.googletagmanager.com/gtag/js?id=G-GOOGLEID" />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){window.dataLayer.push(arguments);}
          gtag('js', new Date());

          gtag('config', 'G-GOOGLEID');
        `}
      </Script>
    </>
 );
}

And then, in your app/layout.tsx file, simply import the component like this:

import './globals.css'
import type { Metadata } from 'next'
import { GoogleAnalyticsTracking } from '@/components/GoogleAnalytics'

export const metadata: Metadata = {
  title: 'Your app name',
  description: 'App description'
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <GoogleAnalyticsTracking />
      <body>{children}</body>
    </html>
  )
}

In my setup, I'm using Nextjs with CSR (client-side rendering) by using "build": "next build && next export" in package.json. This solution was inspired by this other post: Inserting script in head section of Next.js using Script component

Cnidoblast answered 17/9, 2023 at 17:41 Comment(0)
M
2

Make sure you are actually using your layout in in your _app.js file:

// _app.js
import '../styles/globals.css';
import Layout from './layout';

function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

export default MyApp;

In your layout.tsx you can load the script as follows:

// layout.js
import Head from 'next/head';

export default function Layout({ children }) {
  return (
    <>
      <Head>
        <link rel="preconnect" href="https://cdn.sanity.io/" />
        <link rel="dns-prefetch" href="https://cdn.sanity.io//" />
      </Head>
      <script
        id="google-tag-manager"
        dangerouslySetInnerHTML={{
          __html: `(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=%27+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-xxxx');`,
        }}
      />
      <main>{children}</main>
    </>
  );
}

Note that I'm not using the Next Script component, because the docs don't seem to mention anything about using children with the component.

Here is the full working example on Stackblitz.

Also make sure you don't have any browser extensions enabled which remove tracking related scripts. You can test in a different browser or in incognito mode.

Maui answered 30/4, 2023 at 18:54 Comment(1)
Thank you for your answer. I've added an Update to my question with more detail (there is no _app.js in the project). Also, I couldn't see your change in <script id="google-tag-manager" in` layout.tsx.Rubberize
O
2

The @magicul/next-google-tag-manager node package may have removed the need to build something yourself:

app/layout.tsx

import GoogleTagManager from '@magicul/next-google-tag-manager';
const RootLayout = ({ children }) => (
  <html lang="en">
    <body>
      <GoogleTagManager id="GTM-XXXXX" />
      {children}
    </body>
  </html>
);
Oppugnant answered 26/9, 2023 at 18:35 Comment(1)
This is by far the easiest solutionAphrodisia
S
1

You can easily add Google Analytics to your Next.js app by using @next/third-parties/google library which is provided by Next.js.

  1. First of all open your analytics account and create new Google Analytics 4 property. At the data collection step it will provide you a measurement id like (G-XXXXX). Copy this ID and open your Next.js project.

  2. Add Next.js third parties library to your project

    yarn add @next/third-parties

  3. After import as per the NextJs documentation guide

  • To load Google Tag Manager for all routes, include the component directly in your root layout and pass in your GTM container ID:

app/layout.js

import { GoogleTagManager } from '@next/third-parties/google'
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleTagManager gtmId="G-XXXXXXXX" />
    </html>
  )
}
  • To load Google Tag Manager for a single route, include the component in your page file:

app/page.jsx

import { GoogleTagManager } from '@next/third-parties/google'
 
export default function Page() {
  return <GoogleTagManager gtmId="G-XXXXXXXX" />
}
  1. Run your app and inspect. You will be able to see the script tag of google analytics in your page elements.

Make sure to test data stream URL after code update in google analytics. It will take hours for receiving analytics.

Salcido answered 13/1, 2024 at 15:5 Comment(3)
When i do ctrl+u then i can't see tag manager code in source. is there any way i can see in view source?Teri
After ctrl +u click on line-wrap checkbox on top of the page . Then press ctrl+f and type "gtmId". you can see your gtmid in your view sourceSalcido
I tried it and gtmid is not there anywhere, but i can see in "Inspect Element", i am creating build using standalone option is this the reason for not showing?Teri
A
1

To set up GTM with Next.js App router you need a few things:

  1. GTM account: https://tagmanager.google.com
  2. Container ID, e.g. GTM-AB2CDEF
  3. @next/third-parties package from Next.js

After you've created your Google Tag Manager account and acquired a Container ID, install the @next/third-parties library:

pnpm install @next/third-parties@latest next@latest

To load GTM for all routes in your Next.js application, include the <GoogleTagManager /> component in your root layout, making sure to pass the required Container ID.

import { GoogleTagManager } from '@next/third-parties/google'

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <GoogleTagManager gtmId="GTM-XXXXXXX" />
      <body>{children}</body>
    </html>
  );
}

To help clear up any confusion about what the root layout is I have attached a screenshot from a clean create-next-app installation.

For more on this topic please refer to my latest guide on how to add GTM to Next.js App Router, there's a video walkthrough, and a downloadable repo containing the setup.

root layout from create-next-app

Apotheosize answered 4/6, 2024 at 17:19 Comment(0)
A
0

Next 13 App Dir Solution after days and reading many articles i share my solution, (i mention that before using this solution I took next__error in my production which made me frustrated because i use tag straight in the layout.jsx for some reasons) and mention that i use the package GtmHandler for this issue but you can do it manually like here

let's start and create the instance in the components directory

app/components/GTMHandler.jsx

"use client"

import GoogleTagManager from "@magicul/next-google-tag-manager"
import { Suspense } from "react"

const GTMHandler = ({ id }) => {
// id is passed from parent
    return (
        <Suspense>
            <GoogleTagManager id={id} />
        </Suspense>
    )
}

export default GTMHandler

then in layout.jsx use it

app/layout.jsx

import GTMHandler from "../components/GTMHandler";


export default async function RootLayout({ children }) {
let id = "YOUR__ID"
  return (
    <html lang="fa" dir="rtl">
      <head>
        <meta name="robots" content="noindex,nofollow" />
          <GTMHandler id={id} />
      </head>
      <body>{children}</body>
    </html>
  );
}

the Suspense HOC caused my app do not crash in production and works properly

Alisander answered 20/11, 2023 at 9:0 Comment(0)
O
0

Here i found solution

it worked for me guys

here where i get the solution

https://pavelbrecik.cz/the-easiest-cro-you-can-do-is-via-google-tag-manager.html

I added this code to layout.js

Note: Dont forget add setTimeout

  <script dangerouslySetInnerHTML={{
      __html: `
      setTimeout(function() {
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'xxxxxxxx');

        // Additional event pushed after GTM initialization
        dataLayer.push({'event': 'afterLoad'});
      }, 1500);
    `
    }}>
    </script>
Obliteration answered 9/2, 2024 at 8:35 Comment(0)
F
0

I tried a lot of solutions and this one works form me:

in layout.tsx:


export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html
      lang="en"
      className={`${futura.variable} ${poppins.variable} ${italic.variable}`}
    >
      <link rel="shortcut icon" href="/favicon.ico" />
      <body>
        <main>{children}</main>
        <GoogleTagManager gtmId="GTM-XXXXXXXX" />
      </body>
    </html>
  );
}

I hope can help you!

Footless answered 26/6, 2024 at 18:56 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.