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?
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;
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With