How to add Google Analytics 4 to Next.js

By

Learn how to add Google Analytics 4 to Next.js by tracking route changes with gtag in a _app.js useEffect and injecting the GA script from _document.js.

~~~

To add Google Analytics 4 to a Next.js site you need two pieces: the GA script tag, injected once from a custom document, and a route change listener in _app.js that tells GA about client-side navigations.

Why the second piece? Next.js is a single page application after the first load. When visitors click a link, the page doesn’t reload, so the GA script alone would only record the first pageview. Every navigation after that would be invisible to your analytics.

Set up the property ID

Create a Google Analytics property and save the property ID (it looks like G-XXXXXXXXXX) in a NEXT_PUBLIC_GOOGLE_ANALYTICS environment variable, in your .env.local file.

The NEXT_PUBLIC_ prefix matters. Next.js only exposes environment variables to the browser when they start with it. Name the variable GOOGLE_ANALYTICS and process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS will be undefined in the client code, and tracking silently won’t work. That’s the most common reason this setup fails.

Track route changes in _app.js

Then, you need to add a useEffect() call in the pages/_app.js file, which might look like this now:

import '/public/style.css'

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

Change it to

import { useEffect } from 'react'
import { useRouter } from 'next/router'

import 'tailwindcss/tailwind.css'
import '/public/style.css'

function MyApp({ Component, pageProps }) {
  const router = useRouter()

  useEffect(() => {
    const handleRouteChange = url => {
      window.gtag('config', process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS, {
        page_path: url,
      })
    }
    router.events.on('routeChangeComplete', handleRouteChange)
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
    }
  }, [router.events])

  return <Component {...pageProps} />
}

export default MyApp

Every time a client-side navigation completes, Next.js fires the routeChangeComplete event. Our handler calls gtag('config', ...) with the new URL, which registers a pageview in GA. The cleanup function removes the listener when the component unmounts, so we never attach it twice.

Inject the GA script from _document.js

Finally, add a pages/_document.js file that creates a Next.js custom document that injects the Google Analytics script, with:

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

export default class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <script
            async
            src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}`}
          />
          <script
            dangerouslySetInnerHTML={{
              __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}', {
              page_path: window.location.pathname,
            });
          `,
            }}
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

The first script loads the gtag library from Google. The second one, inlined with dangerouslySetInnerHTML, defines the gtag() function and records the initial pageview. That covers the first load, and the _app.js listener covers everything after.

To verify it works, deploy and open the Realtime report in Google Analytics while you click around your site. Don’t trust the regular reports for testing, they can take a day to show data.

Note: solution adapted from a post by Marie Starck on https://mariestarck.com/add-google-analytics-to-your-next-js-application-in-5-easy-steps/

Tagged: Next.js · All topics
~~~

Related posts about next: