I'm using styled-components with next.js so my styles need to be server-side rendered, hence how can I add google analytics to my website?
I checked next.js google analytics example but as I said my _document file is different because of using styled-components.
// _document.js
import React from 'react'
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () => originalRenderPage({
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
}
} finally {
sheet.seal()
}
}
}
export default MyDocument
To correctly initialize gtag
, do the following in _document.js
or wherever you defined Head
:
import Document, { Head } from "next/document";
export default class MyDocument extends Document {
render() {
return (
// ...
<Head>
<script
async
src="https://www.googletagmanager.com/gtag/js?id=[Tracking ID]"
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '[Tracking ID]', { page_path: window.location.pathname });
`,
}}
/>
</Head>
);
}
}
The above will track page views on page load. To track navigation add the following to _app.js
:
import { useRouter } from 'next/router';
import { useEffect } from "react";
export default const App = () => {
const router = useRouter();
const handleRouteChange = (url) => {
window.gtag('config', '[Tracking ID]', {
page_path: url,
});
};
useEffect(() => {
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return (
// ...
);
};
See also:
I'm using below setup for my personal site ( https://github.com/GorvGoyl/Personal-Site-Gourav.io ) and it's working fine without any linting errors. Analytics is enabled only for production.
/lib/gtag.ts
file and add your Google Measurement ID:export const GA_TRACKING_ID = "<INSERT_TAG_ID>";
// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
export const pageview = (url: URL): void => {
window.gtag("config", GA_TRACKING_ID, {
page_path: url,
});
};
type GTagEvent = {
action: string;
category: string;
label: string;
value: number;
};
// https://developers.google.com/analytics/devguides/collection/gtagjs/events
export const event = ({ action, category, label, value }: GTagEvent): void => {
window.gtag("event", action, {
event_category: category,
event_label: label,
value,
});
};
types
:npm i -D @types/gtag.js
/pages/_document.tsx
:import Document, { Html, Head, Main, NextScript } from "next/document";
import { GA_TRACKING_ID } from "../lib/gtag";
const isProduction = process.env.NODE_ENV === "production";
export default class MyDocument extends Document {
render(): JSX.Element {
return (
<Html>
<Head>
{/* enable analytics script only for production */}
{isProduction && (
<>
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
</>
)}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
/pages/_app.tsx
:import { AppProps } from "next/app";
import { useRouter } from "next/router";
import { useEffect } from "react";
import * as gtag from "../lib/gtag";
const isProduction = process.env.NODE_ENV === "production";
const App = ({ Component, pageProps }: AppProps): JSX.Element => {
const router = useRouter();
useEffect(() => {
const handleRouteChange = (url: URL) => {
/* invoke analytics function only for production */
if (isProduction) gtag.pageview(url);
};
router.events.on("routeChangeComplete", handleRouteChange);
return () => {
router.events.off("routeChangeComplete", handleRouteChange);
};
}, [router.events]);
// eslint-disable-next-line react/jsx-props-no-spreading
return <Component {...pageProps} />;
};
export default App;
More info: https://gourav.io/blog/nextjs-cheatsheet
In your _document.js
you override the getInitialProps
method. You can also override the render
method. Simply add
render() {
return (
<Html lang={this.props.lang || "en"}>
<Head>
<script
dangerouslySetInnerHTML={{
__html: `[google analytics tracking code here]`
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
Make sure you import the required components:
import Document, { Html, Head, Main, NextScript } from "next/document"
Do not use the top answer here : using the native <script>
tag is forbidden and it should be defined outside of the <head>
tag .
This is the proper way to include a script tag and configure up Google Analytics in NextJS:
import Script from 'next/script'
import Head from 'next/head'
export default function Index() {
return (
<>
<Head>
<title>Next.js</title>
</Head>
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
</>
)
}
For more info: https://nextjs.org/docs/messages/next-script-for-ga
This is the method recommended by next.js .
/components/GoogleAnalytics.jsx
import Script from 'next/script'
import { useEffect } from 'react'
import { useRouter } from 'next/router'
const GA_TRACKING_ID = '...'
export default () => {
const router = useRouter()
useEffect(() => {
const handleRouteChange = url => {
window.gtag('config', GA_TRACKING_ID, { page_path: url })
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<>
<Script
strategy='afterInteractive'
src={`https://www.googletagmanager.com/gtag/js?id=${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', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`
}}
/>
</>
)
}
/pages/_app.jsx
import GoogleAnalytics from './../components/GoogleAnalytics'
export default function App ({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
{
process.env.NODE_ENV === 'production' &&
<GoogleAnalytics />
}
</>
)
}
Another way that worked well to me without dangerouslySetInnerHTML
:
dangerouslySetInnerHTML
._document.js
file. The sample for what my _document.js
returned:
<Html>
<Head>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-xxx-x"></script>
<script src="/ga.js" async></script>
{/*other scripts*/}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
It is little differend question, but I found quick and easy solution on this page: https://www.learnbestcoding.com/post/9/easiest-way-to-integrate-google-analytics-with-react-js-and-next-js . You can use App component instead of custom Document. All you need to do is install react-gtm-module and then add useEffect to your App component. Final page can look like this:
import '../styles/globals.css';
import Layout from "../components/Layout";
import Head from "next/head";
import {useEffect} from "react";
import TagManager from "react-gtm-module";
function MyApp({ Component, pageProps }) {
useEffect(() => {
TagManager.initialize({ gtmId: 'GTM-XXXXX' });
}, []);
return(
<Layout>
<Head>
...
</Head>
<Component {...pageProps} />
</Layout>
)
}
export default MyApp
In GTM-XXXXX will be your generated ID from Google Tag Manager. When you are done with GTM, then just connect it with your Google Analytics with your tag.
refer to this documentation: https://nextjs.org/docs/api-reference/next/script .
in your pages/_app.js
:
import Script from 'next/script'
...
function MyApp({Component, pageProps}) {
return (
<div>
...
<Script
id="google-analytics"
src="https://www.googletagmanager.com/gtag/js?id=YOUR-ID"
onLoad={() => {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'YOUR-ID');
}}
/>
</div>
)
}
The selected answer only fires once per full refresh of the browser. It does not fire for subsequent internal route changes using Link from "next/link"
. For example:
www.yourdomain.com/page_1
in his browser and hits enter (or maybe he clicked on your website result on Google)./page_1
<Link>
from "next/link"
to navigate to another page, /page_2
/page_2
. It will only re-fire on a full refresh, like when you refresh the browser by hitting F5.This might be ok for some cases. But I think most people would like it to fire once every page change.
Here is what I'm using to fire on every pathname
change.
_app.tsx
const App: React.FC<CustomAppProps> = ({ Component, pageProps }) => {
useLogPageView();
return (
<>
<Layout> // Layout includes Header Main and Footer for my pages
<Component {...pageProps}/> // Component is rendered inside Main
</Layout>
</>
);
};
export default App;
useLogPageView.ts
import { useEffect } from "react";
import { useRouter } from "next/router";
export const useLogPageView = () : void => {
const router = useRouter();
const { pathname, asPath } = router;
// IF YOU ARE USING DYNAMIC ROUTES LIKE /posts/[slug]
// THEN YOU SHOULD USE asPath INSTEAD OF pathname
// THIS EFFECT WILL RUN ON EVERY asPath CHANGE
useEffect(() => {
gtag('config', '${GA_TRACKING_ID}', { // DON'T ADD THIS TO _document.tsx
page_path: window.location.pathname, // OTHERWISE YOU'LL GET DUPLICATE HITS
}); // ON FIRST PAGE LOAD
},[asPath]);
};
Checkouthttps://nextjs.org/docs/api-reference/next/router#router-object
You need first to get your google analytics id through google then create _document.js in pages folder if it is not created yet and copy this code
_document.js
import Document, { Html, Head, Main, NextScript } from "next/document"; import { G_TAG } from "../lib/constants"; export default class MyDocument extends Document { render() { const url = "https://www.googletagmanager.com/gtag/js?id=" + `${G_TAG}`; return ( <Html lang="en"> <Head> <script async src={`${url}`}></script> <script dangerouslySetInnerHTML={{ __html: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', '${G_TAG}', { page_path: window.location.pathname, }); `, }} /> </Head> <body> <Main /> <NextScript /> </body> </Html> ); } }
you need then to define your G_TAG depending on environment like this:
constants.js
you can check step by step how to create your google analytics ID and set it up with Next.js in: https://learnjsx.com/category/4/posts/nextjs-ganalyticsexport const G_TAG = { development: "dev-mode", production: "YOUR-MEASUREMENT-ID-FROM-GANALYTICS", }[process.env.NODE_ENV];
A better and recommended way by Nextjs team is to use next/script
This way execution is deferred till necessary
import Script from 'next/script'
const Home = () => {
return (
<div class="container">
<!-- Global site tag (gtag.js) - Google Analytics -->
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
</div>
)
}
export default Home
Nextjs article with details https://nextjs.org/docs/messages/next-script-for-ga
This Answer from a GitHub issue helped me
With React hooks :
_app.js
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import ReactGA from 'react-ga'
import Layout from '../components/Layout'
function MyApp ({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url, { shallow }) => {
ReactGA.set({ page: url })
ReactGA.pageview(url)
}
ReactGA.initialize('XX-XXXXXXXXX-X', { debug: false })
ReactGA.set({ page: router.pathname })
ReactGA.pageview(router.pathname)
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [])
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default MyApp
Credits to @RiusmaX. Cheers!!
process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS=G-FEWE...
import '../styles/globals.css'
import Script from 'next/script'
function MyApp({ Component, pageProps }) {
return (
<>
<Script strategy="lazyOnload" src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}`} />
<Script id="google-analytics-script" strategy="lazyOnload">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}', {
page_path: window.location.pathname,
});
`}
</Script>
<Component {...pageProps} />
</>
)
}
export default MyApp
process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS=G-FEWE...
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import Script from 'next/script'
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Script strategy="lazyOnload" src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}`} />
<Script id="google-analytics-script" strategy="lazyOnload">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS}', {
page_path: window.location.pathname,
});
`}
</Script>
<Component {...pageProps} />
</>
)
}
export default MyApp
It's how I use my next.js apps.
I have created a React component
that I normally just copy/paste to integrate Google analytics into a new project. Full process takes around 2 minutes. Just follow these 2 steps:
Create a React component named GoogleAnalytics.jsx
or GoogleAnalytics.tsx
with the following content. Just replace the GA_TRACKING_ID
variable with your Google analytics tracking ID.
import { useRouter } from 'next/router';
import Script from 'next/script';
import { useEffect } from 'react';
// **Important** Replace this tracking ID by your Analytics code
// Or you can put it into the environment file.
const GA_TRACKING_ID = 'G-XXXXXXXXX';
// @ts-ignore
const addPageView = (url) => {
// @ts-ignore
window.gtag('config', GA_TRACKING_ID, {
page_path: url,
});
};
const GoogleAnalytics = () => {
const router = useRouter();
useEffect(() => {
// @ts-ignore
const handleRouteChange = (url) => {
addPageView(url);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${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', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
</>
);
};
export default GoogleAnalytics;
Then just inject this component in your _app.js
or _app.ts
. No need to touch anything else and you are done
import '../styles/globals.css'
import GoogleAnalytics from 'path-of-the-GoogleAnalytics-component'
function MyApp({ Component, pageProps }) {
return (
<>
<GoogleAnalytics />
<Component {...pageProps} />
</>
);
}
export default MyApp;
_app.js
& also less modification required._app.js
file. Because we are listening on routeChangeComplete
so this triggers on every page chages.Notes:
Next.js
project.Next.js
version 12 & 13.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.