How to load Google Tag Manager with next/script component (Next.js 11)?

Viewed 4752

Next.js v11 released a new Script component which has different strategies.

It is recommended to load Google TagManager with afterInteractive strategy.

I've tried

// _app.js

import Script from 'next/script';

class MyApp extends App {
  public render() {
    const { Component, pageProps } = this.props;

    return (
      <>
        <Script 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>
        <Component {...pageProps} />
      </>
    );
  }
}

export default MyApp;

It works fine, it loads google tag manager, but the problem is that it injects the same script on every page nav, which makes duplicate tags.

How to utilize the new Script component?

3 Answers

I think that you should set an id to your <Script> component.

Next can check if it has been already rendered or not and you will not have these duplications.

This is the eslint rule associated from Next :

next/script components with inline content require an id attribute to be defined to track and optimize the script.

See: https://nextjs.org/docs/messages/inline-script-id

So your solution could be :

    <Script id="gtm-script" strategy="afterInteractive">{`...`}</Script>

You should probably also install eslint for your next project :
Either run next lint or install eslint next config :

yarn add -D eslint eslint-config-next

and define the file .eslintrc.json with this content at least :

{
  "extends": ["next/core-web-vitals"]
}

Information about next.js eslint configuration.

My final solution was to break apart the GTM script.

Putting the initial dataLayer object on the window in _document page.

// _document.js

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

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>
          <meta charSet="utf-8" />

          <script
            dangerouslySetInnerHTML={{
              __html:
                `(function(w,l){` +
                `w[l] = w[l] || [];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});` +
                `})(window,'dataLayer');`,
            }}
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

Loading the GMT script with Script component (which is not allowed to be used in the _document page)

// _app.js

import Script from 'next/script';

class MyApp extends App {
  public render() {
    const { Component, pageProps } = this.props;

    return (
      <>
        <Script src={`https://www.googletagmanager.com/gtm.js?id=GMT-XXXXXXX`} />
        <Component {...pageProps} />
      </>
    );
  }
}

export default MyApp;
Related