Home > References > SEO

Next js google tag manager: 3 ways to use Google analytics in Nextjs

Last updated : May 28, 2022

Using Google tag manager with Next js takes two simple steps.

  1. Place GTM scripts in the <head> of a shared page.
  2. Create a history change trigger in the Google Tag Manager (GTM console)
There are three ways to place GTM scripts.
  1. Place Google provided scripts on a shared layout page
  2. Use react-gtm-module npm package (No GTM scripts necessary)
  3. Directly triggering GTM scripts (no history change trigger needed)
This tutorial discusses two ways to place and trigger GTM scripts. To implement GTM without a History Change Trigger (third way), check Google Analytics 4 Next.js Template without History Change Trigger. This method also addresses the Multiple installations of google tag manager detected warning.

Google tag manager is inherently designed with traditional request-response based websites in mind. If you use client-side rendering technologies like React js or Next js to server-side render a client-side application, you may already have noticed that google tag manager will not work out of the box. But Google tag manager has a built-in solution for this.

1. Have your Analytics Scripts ready

If you already have created a Google Analytics account, you should have two analytics tracking tags ready. If you haven't already done so, head on to https://tagmanager.google.com and create an account. Upon account creation, you will be presented with two javascript tags.

/*Google Tag Manager - as high in the <head> of the page*/
<script>(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-XXXXXXX');</script>
<!-- End Google Tag Manager -->
/*Google Tag Manager (noscript)*/
<!--immediately after the opening <body> tag-->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

These two tags need to be placed high in the of the page and immediately after the opening tag.

2. Where to put the analytics script tags?

There are two ways to implement google tag manager in Next js. However, both methods require a History change trigger in the google tag manager console. Check the article Google Analytics 4 Next.js Template without History Change Trigger on how to do it without a History Change Trigger.

  • 1. Inserting gtm scripts in header and page (as suggested by Google)
  • 2. Using npm package react-gtm-module (no script insertion needed)

Below we discuss both methods in detail. Ensure to implement only one of these methods to avoid duplicate gtm tags.

2.1 Inserting gtm scripts in header and page

Depending on your Next js app architecture, you can place the above scripts somewhere in _app.js, layout.js, or _document.js. Make sure to place it in a resource shared by every page in your application. If you have a _document.js, you can place the script in the <Head/> section of the _document.js. You can use the below script tag to insert scripts inline without creating additional javascript files. Ensure to place the <script> and <noscript> tags in the proper position.

For Nextjs 11 and up, use the next/script tag to insert the script into the page. The <Script> tag provides several advantages such as lazy loading preferences. More information here.

2.1.1 For Nextjs version 11 and up

<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-XXXXXXX');`}}></Script>

2.1.2 For Nextjs version 10 and below

<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='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');`}}></script>

Now place the page script at the beginning of the body tag. Ensure this is visible to every page in your application.

2.1.3 Insert page script inline

<noscript dangerouslySetInnerHTML={{ __html: `<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe>`}}></noscript>

The below example shows the scripts inserted in the _document.js file. You may select the script location based on your Next app architecture.

2.1.4 Tag manager scripts placed in _document.js

import Document, { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
class WebDocument extends Document {
  render() {
    return (
      <Html lang="en-US">
        <Head>
        <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-XXXXXXX');`}}></Script>
        </Head>
        <body>
            <noscript dangerouslySetInnerHTML={{ __html: `<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXX"
            height="0" width="0" style="display:none;visibility:hidden"></iframe>`}}></noscript>
          <Main/>
          <NextScript />
        </body>
      </Html>
    )
  }
}
export default WebDocument

We are not done yet. If you decide to go ahead with this method, jump on to configure the History change trigger.

2.2 Using npm package react-gtm-module

Using react-gtm-module is the easiest and cleanest way to implement the gtm in Next js. There are two easy steps to this.

2.2.1 Installing react-gtm-module package

First, you will have to include the react-gtm-module in the package.json file. To do that, open a command prompt and navigate to your app root folder, and type

npm install react-gtm-module --save

or

edit your package.json file and add "react-gtm-module": "^2.0.11" as a dependency.

2.2.2 Inserting the tracking code

Now pick a shared page (as mentioned in the google page scripts option above) and insert the gtm tracking code. TagManager.initialize({ gtmId: 'GTM-XXXXX' }); For example, the below code illustrates the react gtm implementation in the _app.js file.

2.2.3 react-gtm-module configured in _app.js

import { useEffect } from 'react';
import TagManager from 'react-gtm-module';

function MyApp({ Component, pageProps }) {
  useEffect(() => {
      TagManager.initialize({ gtmId: 'GTM-XXXXX' });
  }, []);
  return <Component {...pageProps}/>
}
export default MyApp

3. The problem: Google Analytics not tracking internal page navigations

So far, everything seems to be in place. When you enter your website by typing the URL, you will notice that google analytics tracks the visit. But once you start navigating within your website by clicking react or next <Link> elements, you will not see any subsequent clicks registered. In other words, Google is unable to track page navigations occur within our website. Therefore, Google can track only initial page loads where a full page load occurs.

4. Why Google Analytics does not track internal page navigations

The reason is React js or Next js does a full page load only when the page is loaded initially. They do not refresh the page in consequent navigations. That's the correct behavior. That's how we experience those seamless and efficient page transitions. Therefore, our google tracking scripts are executed only once, when we initially visit the page.

5. The solution: Add a history change trigger

The easiest solution is to add a history change trigger in the Google tag manager console. We can tackle the code on our application side and implement a solution. But there is a built-in solution for this in the Google tag manager console. The History Change trigger can capture all the navigations perform on the client-side using React or Next router links.

  • Log into google tag manager.
  • Click Triggers
Google tag manager triggers
Figure 1 : Adding a trigger in Google tag manager
  • Click New -> Trigger Configuration and select History Change trigger.
History Change trigger
Figure 2 : Trigger Configuration Select History Change trigger
  • Make sure to click All History Changes.
  • Name the trigger and click save.
Saving History Change trigger
Figure 3 : Give a name and save the trigger

Don't forget to publish your workspace container.
That's all to it. Now start a debug session by clicking the Preview button on the top right. Once you click start, the tag manager will open a debug window connected to your website. You can use this window to navigate your website links.

Google tag manager debugger
Figure 4 : Srart a debug session by clicking the Preview button on the top right

You will notice that there is a History created for every page transition that happens within the website. Every page refresh will result in Container, DOM, and Window events.

Google tag manager history change
Figure 5 : Google tracks every transition by creating a history event

6. Conclusion

Nextjs does a full page load only when the page is loaded initially. It does not refresh the page in consequent navigations. Therefore, the Google Analytics tracking scripts are executed only at the initial page load. To overcome this, we can use Google's History Change trigger. The History Change trigger fires when the URL fragment changes or when the site uses the HTML 5 push state API.

L Raney
By: L Raney
Lance is a software engineer with over 15 years of experience in full-stack software development.
Read more...

Comments are disabled

No Comments