Cover image for Add Google Analytics through GTM (Google Tag Manager) on Next.js
Ornio AS

Add Google Analytics through GTM (Google Tag Manager) on Next.js

oxodesign profile image Flamur Mavraj ・3 min read

Last week we released a brand new website (https://uwork.no) for our amazing client (uWork AS) and had to get Google Tag Manager (GTM) up and running, easy, we thought! Well, not quite!

For those interested, the stack is:

  • Next.js
  • TailwindCSS
  • Laravel (API)
  • Kubernetes (K8S) hosted on DigitalOcean
  • Bitbucket with Bitbucket Pipelines for deployment

The problem

Since we had GTM already setup, we thought all we needed to do is to inject the GTM script. And so we did!

We checked if the script was loaded ✅, we also checked if GTM was loaded correctly using Google Tag Manager extension for Chrome ✅, yep everything looked OKEY!

Then after completed the checklist before release we pushed to PROD and run DEPLOY 🚀😎

Next day we where monitoring Google Analytics and found out that all the data where from the index page (path: /), so we started to investigate and found out:

  • Moving from page to page did not fire our GA tag!

The solution

After using the whole day researching and reading on internet and found different suggestions on how to solve this, below we will show you our implementation using the classic Google Analytics tag.

Inject GMT script in pages/_document.tsx:

    {/* Google Tag Manager */}
            __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],
    {/* End Google Tag Manager */}
    {/* Google Tag Manager (noscript) */}
            __html: `<iframe src="https://www.googletagmanager.com/ns.html?id=${GTM_ID}" height="0" width="0" style="display:none;visibility:hidden"></iframe>`,
    {/* End Google Tag Manager (noscript) */}

Remember to replace / define GTM_ID!

Then create an page view util function, utils/gtm.ts:

export const GTMPageView = (url: string) => {
    interface PageEventProps {
        event: string;
        page: string;

    const pageEvent: PageEventProps = {
        event: 'pageview',
        page: url,
    window && window.dataLayer && window.dataLayer.push(pageEvent);
    return pageEvent;

After that we need to run "pageview" event on every page, this can be done by setting up an listener (routeChangeComplete) on our Router using useEffect, pages/_app.tsx:

import { AppProps } from 'next/app';
import Router from 'next/router';
import React, {useEffect } from 'react';
import { GTMPageView } from '../utils/gtm';

function MyApp({ Component, pageProps }: AppProps) {
    // ...

    // Initiate GTM
    useEffect(() => {
        const handleRouteChange = (url: string) => GTMPageView(url);
        Router.events.on('routeChangeComplete', handleRouteChange);
        return () => {
            Router.events.off('routeChangeComplete', handleRouteChange);
    }, []);

    // ...

export default MyApp;

Almost done, now we need to add a new Custom Event to fire our GA tag.

In GTM console:

  1. Go to Triggers and click "New"
  2. Name it as desired, in our case I'm naming it "PageViewCustom"
  3. Under "Trigger configuration" select "Custom event", and under "Event name" write "pageview" <- this should the same as defined in your code/function GTMPageView. alt text
  4. Then go to Tags find you GA tag and configure to use the event we just created above. alt text
  5. Test the changes and publish it :)

UPDATE: Another alternative

You can as well use "History Change" trigger to achieve the same result.

That's all! Happy tracking 🙃


Editor guide
hukken profile image
Øystein Hukset

Hi. Great tip!
But I see a problem on my end using this method. The GTM script renders multiple times (for each Page Change). Does not matter if I use the Custom Event trigger or the History Change trigger.

Anybody else seeing this?

toyurc profile image
Adebayo-Ige Toyosi

I have been having this same issue for a while now, thanks a lot for this

abranhe profile image
Abraham Hernandez

Thanks for this!!!!!

dbredvick profile image

I did this awhile back and forgot to make the event "non-interactive" and our bounce rate dropped a TON.

Oops 😅