Using Google tag manager with Next js takes two simple steps.
- Place GTM scripts in the <head> of a shared page.
- Create a history change trigger in the Google Tag Manager (GTM console)
- Place Google provided scripts on a shared layout page
- Use react-gtm-module npm package (No GTM scripts necessary)
- Directly triggering GTM scripts (no history change trigger needed)
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
or
edit your package.json file and add
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.
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
- Click New -> Trigger Configuration and select History Change trigger.
- Make sure to click All History Changes.
- Name the trigger and click save.
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.
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.
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.