Soorena
Soorena

Reputation: 4462

How to set up Google Analytics through Google Tag Manager for Next-Js?

formerly I was using react-ga npm module to insert google analytics in my next js app. and It was simply like this:

import ReactGA from 'react-ga'

export const initGA = () => {
  ReactGA.initialize('UA-*******-*', {
    titleCase: false
  })
}

export const logPageView = () => {
  if (window.location.href.split('?')[1]) {
    ReactGA.set({page: window.location.pathname + '?' + window.location.href.split('?')[1]})
    ReactGA.pageview(window.location.pathname + '?' + window.location.href.split('?')[1])
  } else {
    ReactGA.set({page: window.location.pathname})
    ReactGA.pageview(window.location.pathname)
  }
}

and then I was calling logPageView function in my header(that was inserted to every page of my app) like this:

  componentDidMount () {
    if (!window.GA_INITIALIZED) {
      initGA()
      window.GA_INITIALIZED = true
    }
    logPageView()
  }
  componentWillReceiveProps () {
    if (!window.GA_INITIALIZED) {
      initGA()
      window.GA_INITIALIZED = true
    }
    logPageView()
  }

now I want to use Google Tag Manager to handle Analytics page view . How could I do this?

Upvotes: 27

Views: 43371

Answers (10)

Subhodeep Pal
Subhodeep Pal

Reputation: 1

To use Google Tag Manager in your next js app you should inject your tag manager script on every page. You can do this by creating a _document.js in your pages folder of that project and you have to edit your _app.js to use Google Tag Manager. Here is my _app.js code you can follow this:

const App = ({ Component, pageProps }) => {
  const router = useRouter()
  useEffect(() => {
    const handleRouteChange = (url) => {
      gtag.pageview(url)
    }
    router.events.on('routeChangeComplete', handleRouteChange)
    router.events.on('hashChangeComplete', handleRouteChange)
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
      router.events.off('hashChangeComplete', handleRouteChange)
    }
  }, [router.events])

  return (
    <>
      {/* Global Site Tag (gtag.js) - Google Analytics */}
      <Script
        strategy="afterInteractive"
        src={`https://www.googletagmanager.com/gtag/js?id=${gtag.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', '${gtag.GA_TRACKING_ID}', {
              page_path: window.location.pathname,
            });
          `,
        }}
      />
      <Component {...pageProps} />
    </>
  )
}

export default App

Here is _document.js:

export default function Document() {
    return (
        <Html lang='en'>
            <Head>
                {/* Global Site Tag (gtag.js) - Google Analytics */}
                <script
                    dangerouslySetInnerHTML={{
                        __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
          `,
                    }}
                />
            </Head>
            <body>
                <Main />
                <NextScript />
            </body>
        </Html>
    )
}

Create a gtag.js:

export const GA_TRACKING_ID = '**********'

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

// https://developers.google.com/analytics/devguides/collection/gtagjs/events
export const event = ({ action, category, label, value }) => {
    window.gtag('event', action, {
        event_category: category,
        event_label: label,
        value: value,
    })
}

Upvotes: 0

thisismydesign
thisismydesign

Reputation: 25162

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 id="gtm" strategy="afterInteractive">
        {`
          (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>
      <noscript>
        <iframe
          src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX"
          height="0"
          width="0"
          style={{
            display: 'none',
            visibility: 'hidden',
          }}
        ></iframe>
      </noscript>

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

export default App;

You can see this solution working similarly for gtag 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://stackoverflow.com/a/63249329/2771889

Upvotes: 2

Deen24ID
Deen24ID

Reputation: 361

I recommend an OSS library nextjs-google-analytics as documented in github and published in npm. Its <GoogleAnalytics/> component is a wrapper of the NextJS' <Script/> as mentioned in other answers with additional features such as trackPageViews props which automatically track visitor route changes. It's a small library but IMO makes your codebase neat.

Upvotes: 2

Morgan Feeney
Morgan Feeney

Reputation: 897

Old post, but I recently wrote about this and detailed 2 solutions here: https://morganfeeney.com/how-to/integrate-google-tag-manager-with-next-js.

Prior to Next.js v11.0.0 the simplest way to do this was by using the custom _document.

Add the GTM script like this in a custom _document: https://nextjs.org/docs/advanced-features/custom-document

import Head from 'next/document';
...

<Head>
  <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-XXXX');`,
    }}
  />
</Head>

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

But how do you track page views??

window.onLoad will not fire when navigating using client-side routing, so even though you add the script, it's not doing what you expect it to.

My preferred method is to use a function like this:

export const gtmVirtualPageView = (rest) => {
  window.dataLayer?.push({
    event: 'VirtualPageView',
    ...rest,
  });
};

Then in _app.tsx add the following config:

import '../styles/globals.css';
import { useEffect } from 'react';
import { useRouter } from 'next/router'
import { gtmVirtualPageView } from '../lib/gtm';

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

  useEffect(() => {
    const mainDataLayer = {
      pageTypeName: pageProps.page || null,
      url: router.pathname,
    };

    window.onload = () => window.dataLayer?.push({...mainDataLayer});

    gtmVirtualPageView(mainDataLayer);

  }, [pageProps])

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

export default MyApp

This allows for passing data via pageProps into the dataLayer.

Upvotes: 1

Black
Black

Reputation: 10407

There is no need for any special library and some answers here look uncomplete. Just add the script to <Head> and noscript to <body> in _document.js

import Head from 'next/document'  

<Head>
  <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-XXXXXX');`}} />
</Head>
    
<body style={{ margin: 0 }}>
   <noscript dangerouslySetInnerHTML={{ __html: `<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX"
        height="0" width="0" style="display:none;visibility:hidden"></iframe>`,
      }}
    />
    <Main />
    <NextScript />
</body>

Upvotes: 28

bosznrt
bosznrt

Reputation: 61

Im using react-gtm-module and config in _app.js inside componentDidMount

 componentDidMount() {
    const tagManagerArgs = {
      gtmId = 'GTM-00001'
    }

    TagManager.initialize(tagManagerArgs);
  }

this is my solution updated at Feb 11, 2020

Upvotes: 5

Soorena
Soorena

Reputation: 4462

In order to use Google Tag Manager you should inject your tag manager script on every page. since _document.js is the wrapper for every page, you should add the GTM script to _document.js in the head section like this:

<Head>
  <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-*****');`,
  }}>
  </script>
  ...
</Head>

Now you are all set to use Google Tag Manager in your next-js app. You should only proceed to handle pageview and other analytics related stuff from your GTM dashboard.

To set up google analytics pageview for single page applications(like nextjs) you can follow these steps.

Upvotes: 46

Sagar M
Sagar M

Reputation: 1092

I found a better way of implementing it. We can use react-gtm-module library for it.

As Ryan Elainska mentions in their blog, Google Tag Manager in Next.js in the _app.js file of my NextJS installation:

import App, { Container } from 'next/app'
import React from 'react'
import './app.css'
import TagManager from 'react-gtm'

const tagManagerArgs = {
  id: 'GTM-XXXXXXX'
}

class MyApp extends App {
  componentDidMount () {
    TagManager.initialize(tagManagerArgs)
  }

  render () {
    const { Component, pageProps } = this.props
    return (
      <Container>
        <Component {...pageProps} />
      </Container>
    )
  }
}

export default MyApp

Upvotes: 12

Harry Hur
Harry Hur

Reputation: 31

I'm using redux in my next.js application.

And this is pretty good solution if you're using redux: redux-beacon + react-gtm-module.

here is initialization code. This code can be triggered with hooks or componentDidMount in _app.js or with redux action if you're using redux.

const dataLayer = { ...your dataLayer config... }
const tagManagerArgs = {
  gtmId: process.env.GTM_ID,
  auth: process.env.GTM_AUTH,
  preview: process.env.GTM_PREVIEW,
  dataLayer
}
TagManager.initialize(tagManagerArgs)

below is middleware created with eventMap.

import { EventsMapper, createMiddleware } from 'redux-beacon'
import GoogleTagManager from '@redux-beacon/google-tag-manager'

const gtm = GoogleTagManager()

export const eventsMap: EventsMapper = action => {
  switch (action.type) {
    default:
      return []
  }
}


export const gtmMiddleware = createMiddleware(eventsMap, gtm)

Upvotes: 3

Darryl RN
Darryl RN

Reputation: 8228

Since you are using nextJS, there is no need to add pure javascript from Google Analytics, all you need to have is the GA tracking id.

Then you create util function:

export const event = ({ action, category, label, value }) => {
  window.gtag('event', action, {
    event_category: category,
    event_label: label,
    value: value
  })
}

and add this in your _document.js:

<script
  dangerouslySetInnerHTML={{
  __html: `
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', '${GA_TRACKING_ID}');
`}}
/>

After that, you just need to import the util function in your page/ components and call:

gtag.event({
  action: 'submit_form',
  category: 'Contact',
  label: this.state.message
})

Reference: example of NextJS with GA

Upvotes: 16

Related Questions