Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 395
felixmosh Avatar asked Jun 24 '21 12:06

felixmosh


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.

like image 105
Stephane L Avatar answered Nov 04 '22 19:11

Stephane L


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;
like image 42
felixmosh Avatar answered Nov 04 '22 19:11

felixmosh


Your inline scripts require an "id" parameter, so that Next can internally check and avoid loading the scripts again.

The documentation mentioned it but they missed this in the first release.

It was later added as a patch so upgrading your Next would solve this issue

Patch - https://github.com/vercel/next.js/pull/28779/files/11cdc1d28e76c78a140d9abd2e2fb10fc2030b82

Discussion Thread - https://github.com/vercel/next.js/pull/28779

like image 35
Mudit Jaju Avatar answered Nov 04 '22 19:11

Mudit Jaju