如何在 next.js 应用程序中使用 google 分析?

我在 next.js 中使用样式组件,所以我的样式需要在服务器端呈现,因此我怎样才能将谷歌分析添加到我的网站?

我选中了 Next.js google 分析的例子,但是正如我所说的,由于使用样式化组件,我的 _ document 文件不同。

// _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


82245 次浏览

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"

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
src="https://www.googletagmanager.com/gtag/js?id=G-xxxxxxxxxx"
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());


gtag('config', 'G-xxxxxxxxxx');
`}
</Script>


<Component {...pageProps} />
</>
);
};


export default App;

You can see this solution working 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

Another way that worked well to me without dangerouslySetInnerHTML:

  • Create a new js file in the public folder with the contents in dangerouslySetInnerHTML.
  • Add that created js file to the _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>

To setup Google analytics with NextJS using Typescript

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.

  • Create a Google analytics project and get Measurement ID.
  • In your NextJS project, create /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,
});
};
  • Also install gtag types:
npm i -D @types/gtag.js
  • Create /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>
);
}
}


  • Create /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

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:

  • User enters www.yourdomain.com/page_1 in his browser and hits enter (or maybe he clicked on your website result on Google).
  • That code will fire and send a page view for /page_1
  • User now clicks on a internal <Link> from "next/link" to navigate to another page, /page_2
  • That code will not fire fore /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]);


};

Checkout https://nextjs.org/docs/api-reference/next/router#router-object

enter image description here

enter image description here

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.

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

export const G_TAG = {
development: "dev-mode",
production: "YOUR-MEASUREMENT-ID-FROM-GANALYTICS",
}[process.env.NODE_ENV];
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-ganalytics

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>
)
}

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 />
}
</>
)
}

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 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!!

Steps to use with JavaScript:

  • /.env.local
process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS=G-FEWE...
  • /pages/_app.js
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

Steps to use with TypeScript:

  • /.env.local
process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS=G-FEWE...
  • /pages/_app.js
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:

Step 1

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;

Step 2

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;

Why I think this is better solution for this problem?

  1. All Google analytics integration logic goes to a Separate component. So, less code in _app.js & also less modification required.
  2. This works for all pages even though you just add it only _app.js file. Because we are listening on routeChangeComplete so this triggers on every page chages.

Notes:

  • You can use this same component in all your Next.js project.
  • I have tested it with Next.js version 12 & 13.
  • If you are curious how analytics is send on page change please check this document. Just check the last section.