Reputation: 4462
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
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
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
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
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
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
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
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
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
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
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