How to use google analytics with next.js app?
Asked Answered
K

16

120

I'm using styled-components with next.js so my styles need to be server-side rendered, hence how can I add google analytics to my website?

I checked next.js google analytics example but as I said my _document file is different because of using styled-components.

// _document.js

import React from 'react'
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage

    try {
      ctx.renderPage = () => originalRenderPage({
        enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
      })

      const initialProps = await Document.getInitialProps(ctx)
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      }
    } finally {
      sheet.seal()
    }
  }
}

export default MyDocument

Kwangchowan answered 26/2, 2020 at 10:4 Comment(0)
A
205

Next.js since v11 recommends using their <Script> tag, and the right place to add it is the App component.

pages/_app.jsx

import React from 'react';
import Script from 'next/script';

const App = ({ Component, pageProps }) => {
  return (
    <>
      <Script
        src="https://www.googletagmanager.com/gtag/js?id=G-xxxxxxxxxx"
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){window.dataLayer.push(arguments);}
          gtag('js', new Date());

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

      <Component {...pageProps} />
    </>
  );
};

export default App;

You can see this solution working in nestjs-starter where I'm also setting the tag from an env var.

For v10 and lower use regular <script> tags according to Google's guide.

Keep in mind that Google Analytics does automatic page tracking, but this will not work for every use case. For example, hash and search parameter changes are not tracked. This can lead to a lot of confusion. For example, when using HashRouter or anchor links the navigation will not be tracked. To have full control over page view tracking you can disable automatic tracking. See for a detailed explanation: The Ultimate Guide to Google Analytics (UA & GA4) on React (Or Anything Else

Manual page tracking: https://mcmap.net/q/181480/-how-to-set-up-google-analytics-for-react-router

Australasia answered 24/6, 2020 at 9:50 Comment(19)
Does this register a new page visit on all navigation changes?Agro
It does so on every page load. So as long as you're using Next's router, yes. For client-side navigation, you could look at this: https://mcmap.net/q/181480/-how-to-set-up-google-analytics-for-react-routerAustralasia
The nextjs router doesn't reload the page on navigation changes, though.Agro
I just tested this out. Pageviews are not registered on navigation changes. This is because nextjs doesn't refresh the page on navigation changes, it just changes some props and lets React re-render accordingly.Agro
I'm using next/link and it does register pageviews.Australasia
Do you have a "history change trigger" set up in Google Tag Manager? That would register pageviews on history changes.Agro
Thanks mate, I had same issue, your answer really helped!!Annmarieannnora
Why is it recommended to do in _document.js, if the file is rendered on the back-end only?Protolanguage
I can confirm that this only fires once per browser refresh. No additional hits on subsequent page changes.Strategist
You're right. Updated my answer to track navigation using gtag.Australasia
On Next.js 10.2.2, a page build with the navigation part gives Type error: Property 'gtag' does not exist on type 'Window & typeof globalThis'. for typescript _app.tsx instead of _app.js BTW. Gotta have a look at the Typescript specific answer: https://mcmap.net/q/181404/-how-to-use-google-analytics-with-next-js-appHagiography
I get the warning Use the `next/script` component for loading third party scriptsAnchylose
It appears that the handleRouteChange and useEffect are no longer needed with the newest versions of GA. In fact, it appears that you'll get double the expected page_view events if you're manually listening to router events. The newer tracking IDs start with "GA-".Tenon
If you're having TypeScript errors, try installing @types/gtag.jsHelladic
This doesn't use next/script and throws a warning in Next v12+Toul
@TJMazeika I can confirm this and also think the hook is no longer necessary. In the Google Analytics Debugger in Chrome you can see an event being fired with { event: "gtm.historyChange-v2", gtm.historyChangeSource: "pushState", ... } on client side navigation. Adding the two <Script>s seems sufficient now.Newbill
Strange, it raise me an error: #74064877Fishhook
isn't better strategy="lazyOnload" ?Teatime
@JonathanGruber so now is not necessary to use the manual page tracking, right?Teatime
S
58

To setup Google analytics with NextJS using Typescript

I'm using below setup for my personal site (https://github.com/GorvGoyl/Personal-Site-Gourav.io) and it's working fine without any linting errors. Analytics is enabled only for production.

  • Create a Google analytics project and get Measurement ID.
  • In your NextJS project, create /lib/gtag.ts file and add your Google Measurement ID:
export const GA_TRACKING_ID = "<INSERT_TAG_ID>";

// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
export const pageview = (url: URL): void => {
  window.gtag("config", GA_TRACKING_ID, {
    page_path: url,
  });
};

type GTagEvent = {
  action: string;
  category: string;
  label: string;
  value: number;
};

// https://developers.google.com/analytics/devguides/collection/gtagjs/events
export const event = ({ action, category, label, value }: GTagEvent): void => {
  window.gtag("event", action, {
    event_category: category,
    event_label: label,
    value,
  });
};
  • Also install gtag types:
npm i -D @types/gtag.js
  • Create /pages/_document.tsx:
import Document, { Html, Head, Main, NextScript } from "next/document";

import { GA_TRACKING_ID } from "../lib/gtag";

const isProduction = process.env.NODE_ENV === "production";

export default class MyDocument extends Document {
  render(): JSX.Element {
    return (
      <Html>
        <Head>
          {/* enable analytics script only for production */}
          {isProduction && (
            <>
              <script
                async
                src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
              />
              <script
                // eslint-disable-next-line react/no-danger
                dangerouslySetInnerHTML={{
                  __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${GA_TRACKING_ID}', {
              page_path: window.location.pathname,
            });
          `,
                }}
              />
            </>
          )}
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

  • Create /pages/_app.tsx:
import { AppProps } from "next/app";
import { useRouter } from "next/router";
import { useEffect } from "react";
import * as gtag from "../lib/gtag";
const isProduction = process.env.NODE_ENV === "production";

const App = ({ Component, pageProps }: AppProps): JSX.Element => {
  const router = useRouter();

  useEffect(() => {
    const handleRouteChange = (url: URL) => {
      /* invoke analytics function only for production */
      if (isProduction) gtag.pageview(url);
    };
    router.events.on("routeChangeComplete", handleRouteChange);
    return () => {
      router.events.off("routeChangeComplete", handleRouteChange);
    };
  }, [router.events]);
  // eslint-disable-next-line react/jsx-props-no-spreading
  return <Component {...pageProps} />;
};

export default App;

More info: https://gourav.io/blog/nextjs-cheatsheet

Simpatico answered 30/11, 2020 at 21:29 Comment(6)
Just one thing: the value should not be required in the GTagEvent type and not sent if not provided.Subdiaconate
@MatteoFrana I don't seem to understand your comment above, can you shed more light? ThanksFloe
Kudos! Worked like a charm!Prichard
this is what I needed, thank you !Pyrophyllite
I'd put the GA_TRACKING_ID in an .env file, having it in plain view doesn't seem a good idea to meQuadrumanous
@Quadrumanous the GA tracking ID is meant to be public. It's ok to have others see it.Spoony
I
34

In your _document.js you override the getInitialProps method. You can also override the render method. Simply add

  render() {
    return (
      <Html lang={this.props.lang || "en"}>
        <Head>
          <script
            dangerouslySetInnerHTML={{
              __html: `[google analytics tracking code here]`
            }}
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }

Make sure you import the required components:

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

Ir answered 26/2, 2020 at 10:26 Comment(1)
everyone may want to check my answer of using next/scriptGusset
N
30

Do not use the top answer here: using the native <script> tag is forbidden and it should be defined outside of the <head> tag.

This is the proper way to include a script tag and configure up Google Analytics in NextJS:

import Script from 'next/script'
import Head from 'next/head'

export default function Index() {
  return (
    <>
      <Head>
        <title>Next.js</title>
      </Head>
      <Script
        src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){window.dataLayer.push(arguments);}
          gtag('js', new Date());

          gtag('config', 'GA_MEASUREMENT_ID');
        `}
      </Script>
    </>
  )
}

For more info: https://nextjs.org/docs/messages/next-script-for-ga

Northeastward answered 21/1, 2022 at 22:17 Comment(2)
This is the right way to do this, thank you.Toul
This is also the way provided by Next.js maintainers. github.com/vercel/next.js/tree/canary/examples/…Whitewood
P
13

This is the method recommended by next.js.

/components/GoogleAnalytics.jsx

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

const GA_TRACKING_ID = '...'

export default () => {
  const router = useRouter()
  useEffect(() => {
    const handleRouteChange = url => {
      window.gtag('config', GA_TRACKING_ID, { page_path: url })
    }
    router.events.on('routeChangeComplete', handleRouteChange)
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
    }
  }, [router.events])

  return (
    <>
      <Script
        strategy='afterInteractive'
        src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
      />
      <Script
        id='gtag-init'
        strategy='afterInteractive'
        dangerouslySetInnerHTML={{
          __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${GA_TRACKING_ID}', {
              page_path: window.location.pathname,
            });
          `
        }}
      />
    </>
  )
}

/pages/_app.jsx

import GoogleAnalytics from './../components/GoogleAnalytics'

export default function App ({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />

      {
        process.env.NODE_ENV === 'production' &&
          <GoogleAnalytics />
      }
    </>
  )
}
Propitiatory answered 11/1, 2022 at 22:56 Comment(2)
This results in double the page viewsSalian
This will not work with Nextjs 14 as there are no router.events in Nextjs 14Khadijahkhai
C
6

Another way that worked well to me without dangerouslySetInnerHTML:

  • Create a new js file in the public folder with the contents in dangerouslySetInnerHTML.
  • Add that created js file to the _document.js file.

The sample for what my _document.js returned:

<Html>
  <Head>
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-xxx-x"></script>
    <script src="/ga.js" async></script>
    {/*other scripts*/}
  </Head>
  <body>
    <Main />
      <NextScript />
  </body>
</Html>
Claycomb answered 27/8, 2020 at 3:28 Comment(0)
S
3

It is little differend question, but I found quick and easy solution on this page: https://www.learnbestcoding.com/post/9/easiest-way-to-integrate-google-analytics-with-react-js-and-next-js. You can use App component instead of custom Document. All you need to do is install react-gtm-module and then add useEffect to your App component. Final page can look like this:

import '../styles/globals.css';
import Layout from "../components/Layout";
import Head from "next/head";
import {useEffect} from "react";
import TagManager from "react-gtm-module";

function MyApp({ Component, pageProps }) {

    useEffect(() => {
        TagManager.initialize({ gtmId: 'GTM-XXXXX' });
    }, []);

    return(
      <Layout>
          <Head>
             ...
          </Head>

        <Component {...pageProps} />
      </Layout>
    )
}

export default MyApp

In GTM-XXXXX will be your generated ID from Google Tag Manager. When you are done with GTM, then just connect it with your Google Analytics with your tag.

Stature answered 13/5, 2021 at 18:33 Comment(0)
M
3

Nextjs 14

You can use Google Tag Manager and Google Analytics from nextjs third party libraries.

Google Tag Manager

import { GoogleTagManager } from '@next/third-parties/google'
 
export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />
      <GoogleTagManager gtmId="GTM-XYZ" />
    </>
  )
}

Google Analytics

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

export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />
      <GoogleAnalytics gaId="G-XYZ" />
    </>
  )
}
Marie answered 5/11, 2023 at 19:10 Comment(0)
G
2

refer to this documentation: https://nextjs.org/docs/api-reference/next/script .
in your pages/_app.js:

import Script from 'next/script'

...

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

         <Script
           id="google-analytics"
           src="https://www.googletagmanager.com/gtag/js?id=YOUR-ID"
           onLoad={() => {
             window.dataLayer = window.dataLayer || [];
             function gtag(){dataLayer.push(arguments);}
             gtag('js', new Date());
             gtag('config', 'YOUR-ID');
           }}
         />
      </div>
   )
}
Gusset answered 6/1, 2022 at 23:46 Comment(0)
R
2

I have created a React component that I normally just copy/paste to integrate Google analytics into a new project. Full process takes around 2 minutes. Just follow these 2 steps:

Step 1

Create a React component named GoogleAnalytics.jsx or GoogleAnalytics.tsx with the following content. Just replace the GA_TRACKING_ID variable with your Google analytics tracking ID.

import { useRouter } from 'next/router';
import Script from 'next/script';
import { useEffect } from 'react';

// **Important** Replace this tracking ID by your Analytics code
// Or you can put it into the environment file.
const GA_TRACKING_ID = 'G-XXXXXXXXX';

// @ts-ignore
const addPageView = (url) => {
  // @ts-ignore
  window.gtag('config', GA_TRACKING_ID, {
    page_path: url,
  });
};

const GoogleAnalytics = () => {
  const router = useRouter();

  useEffect(() => {
    // @ts-ignore
    const handleRouteChange = (url) => {
      addPageView(url);
    };
    router.events.on('routeChangeComplete', handleRouteChange);
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router.events]);
  return (
    <>
      <Script
        strategy="afterInteractive"
        src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
      />
      <Script
        id="gtag-init"
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: `
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', '${GA_TRACKING_ID}', {
        page_path: window.location.pathname,
      });
    `,
        }}
      />
    </>
  );
};

export default GoogleAnalytics;

Step 2

Then just inject this component in your _app.js or _app.ts . No need to touch anything else and you are done 🎉

import '../styles/globals.css'
import GoogleAnalytics from 'path-of-the-GoogleAnalytics-component'

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

export default MyApp;

Why I think this is better solution for this problem?

  1. All Google analytics integration logic goes to a Separate component. So, less code in _app.js & also less modification required.
  2. This works for all pages even though you just add it only _app.js file. Because we are listening on routeChangeComplete so this triggers on every page chages.

Notes:

  • You can use this same component in all your Next.js project.
  • I have tested it with Next.js version 12 & 13.
  • If you are curious how analytics is send on page change please check this document. Just check the last section.
Romish answered 1/12, 2022 at 9:46 Comment(0)
S
0

The selected answer only fires once per full refresh of the browser. It does not fire for subsequent internal route changes using Link from "next/link". For example:

  • User enters www.yourdomain.com/page_1 in his browser and hits enter (or maybe he clicked on your website result on Google).
  • That code will fire and send a page view for /page_1
  • User now clicks on a internal <Link> from "next/link" to navigate to another page, /page_2
  • That code will not fire fore /page_2. It will only re-fire on a full refresh, like when you refresh the browser by hitting F5.

This might be ok for some cases. But I think most people would like it to fire once every page change.

Here is what I'm using to fire on every pathname change.

_app.tsx

const App: React.FC<CustomAppProps> = ({ Component, pageProps }) => {

  useLogPageView();
  
  return (
    <>
      <Layout>                       // Layout includes Header Main and Footer for my pages
        <Component {...pageProps}/>  // Component is rendered inside Main
      </Layout>
    </>
  );
};

export default App;

useLogPageView.ts

import { useEffect } from "react";
import { useRouter } from "next/router";

export const useLogPageView = () : void => {

  const router = useRouter();
  const { pathname, asPath } = router;

  // IF YOU ARE USING DYNAMIC ROUTES LIKE /posts/[slug]
  // THEN YOU SHOULD USE asPath INSTEAD OF pathname

  // THIS EFFECT WILL RUN ON EVERY asPath CHANGE
  useEffect(() => {
    gtag('config', '${GA_TRACKING_ID}', {    // DON'T ADD THIS TO _document.tsx
      page_path: window.location.pathname,   // OTHERWISE YOU'LL GET DUPLICATE HITS
    });                                      // ON FIRST PAGE LOAD
  },[asPath]);

};

Checkout https://nextjs.org/docs/api-reference/next/router#router-object

enter image description here

enter image description here

Strategist answered 4/5, 2021 at 16:40 Comment(0)
M
0

You need first to get your google analytics id through google then create _document.js in pages folder if it is not created yet and copy this code

_document.js

import Document, { Html, Head, Main, NextScript } from "next/document";
import { G_TAG } from "../lib/constants";

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

you need then to define your G_TAG depending on environment like this:

constants.js

export const G_TAG = {
  development: "dev-mode",
  production: "YOUR-MEASUREMENT-ID-FROM-GANALYTICS",
}[process.env.NODE_ENV];
you can check step by step how to create your google analytics ID and set it up with Next.js in: https://learnjsx.com/category/4/posts/nextjs-ganalytics
Machute answered 17/10, 2021 at 10:28 Comment(0)
M
0

This Answer from a GitHub issue helped me

With React hooks :

_app.js

import { useEffect } from 'react'
import { useRouter } from 'next/router'
import ReactGA from 'react-ga'
import Layout from '../components/Layout'

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

  useEffect(() => {
    const handleRouteChange = (url, { shallow }) => {
      ReactGA.set({ page: url })
      ReactGA.pageview(url)
    }
    ReactGA.initialize('XX-XXXXXXXXX-X', { debug: false })
    ReactGA.set({ page: router.pathname })
    ReactGA.pageview(router.pathname)
    router.events.on('routeChangeComplete', handleRouteChange)
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
    }
  }, [])

  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

export default MyApp

Credits to @RiusmaX. Cheers!!

Medieval answered 11/2, 2022 at 15:0 Comment(0)
P
0

Steps to use with JavaScript:

  • /.env.local
process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS=G-FEWE...
  • /pages/_app.js
import '../styles/globals.css'
import Script from 'next/script'

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Script strategy="lazyOnload" src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}`} />

      <Script id="google-analytics-script" strategy="lazyOnload">
        {`
                    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,
                    });
                `}
      </Script>

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

export default MyApp

Steps to use with TypeScript:

  • /.env.local
process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS=G-FEWE...
  • /pages/_app.js
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import Script from 'next/script'

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script strategy="lazyOnload" src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}`} />

      <Script id="google-analytics-script" strategy="lazyOnload">
        {`
                    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,
                    });
                `}
      </Script>

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

export default MyApp

It's how I use my next.js apps.

Petition answered 24/6, 2022 at 10:3 Comment(0)
H
0

Create new _document.tsx in your project via: pages/_document.tsx

import { Head, Html, Main, NextScript } from 'next/document';
import Script from 'next/script';

export default function Document() {
  return (
    <Html lang='en'>
      <Head>
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <Script
          src='https://www.googletagmanager.com/gtag/js?id=[YOUR_GA_ID]'
          async
          crossOrigin='anonymous'
          strategy='lazyOnload'
          id='google-tag-manager'
        />
      </Head>
      <body>
        <Main />
        <NextScript />

        <Script id='google-analytics' async strategy='lazyOnload' crossOrigin='anonymous'>
          {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
 
          gtag('config', '[YOUR_GA_ID]');
        `}
        </Script>
      </body>
    </Html>
  );
}
Herring answered 24/10, 2023 at 6:18 Comment(0)
V
0

It can be helpful for someone: I used Script tag in layout.tsx Before using this tag you need to make import

Varityper answered 15/2, 2024 at 3:55 Comment(2)
Images of code should not be used, it's better if the code is actually posted.Spallation
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewCheap

© 2022 - 2025 — McMap. All rights reserved.