image

In this tutorial I'll show you how to add Google Analytics 4 to an existing NextJS Typescript application. We'll start by creating a  GA Tracking ID. Then we'll modify our NextJS  app to use GA. As a bonus, we'll also add typings for gtag.js to enable type safety when adding events.

Create a Google Analytics Tracking ID

Create a GA4 property by going to your admin settings and clicking Create Property.

599fefc5c9212ca6c6b464402.png

Fill-in the details about your website and your business, then you should end up in the data streams panel shown below. Let's create a data stream by first click Web as shown below:

599fefc5c9212ca6c6b464403.png

Fill-in the details and click Create stream:

599fefc5c9212ca6c6b464404.png

After that you should see the Web stream details below and get the Measurement ID:

599fefc5c9212ca6c6b464405.png

Adding the measurement ID to NextJS

Add the measurement ID to your environment variables. In my case I'll add it to a local .env file as shown below.


_10
.env
_10
_10
# replace it th your GA4 tracking ID.
_10
NEXT_PUBLIC_GA4_TRACKING_ID="G-0PMW35MR3C"
_10
123

Adding Google Analytics to NextJS

To avoid the error "window.gtag is not defined for Next.js-hydration", create a file called _document.tsx and include the initialisation script for gtag.


_20
import { Html, Head, Main, NextScript } from "next/document";
_20
_20
export default function Document() {
_20
return (
_20
<Html>
_20
<Head />
_20
<body>
_20
<Main />
_20
<NextScript />
_20
<script
_20
dangerouslySetInnerHTML={{
_20
__html: `
_20
window.dataLayer = window.dataLayer || [];
_20
function gtag(){dataLayer.push(arguments);}
_20
gtag('js', new Date());
_20
`,
_20
}}/>
_20
</body>
_20
</Html>);
_20
}

To handle google analytics, create a component called GoogleAnalytics.tsx and add it to _app.tsx


_49
//components/GoogleAnalytics.tsx
_49
import { useRouter } from "next/router";
_49
import Script from "next/script";
_49
import { memo, useEffect } from "react";
_49
const TRACKING_ID = process.env.NEXT_PUBLIC_GA4_TRACKING_ID!;
_49
const GoogleAnalytics = () => {
_49
const router = useRouter();
_49
_49
useEffect(() => {
_49
if (!TRACKING_ID || router.isPreview) return;
_49
gtag("config", TRACKING_ID, {
_49
send_page_view: false,
_49
gtag("event", "page_view", {
_49
page_path: window.location.pathname,
_49
send_to: TRACKING_ID,
_49
});
_49
}, []);
_49
useEffect(() => {
_49
const handleRouteChange = (url: string) => {
_49
if (!TRACKING_ID || router.isPreview) return;
_49
gtag("event", "page_view", {
_49
page_path: url,
_49
send_to: TRACKING_ID,
_49
});
_49
};
_49
router.events.on("routeChangeComplete", handleRouteChange);
_49
router.events.on("hashChangeComplete", handleRouteChange);
_49
return () => {
_49
router.events.off("routeChangeComplete", handleRouteChange);
_49
router.events.off("hashChangeComplete", handleRouteChange);
_49
};
_49
}, [router.events, router.isPreview]);
_49
_49
if (!TRACKING_ID || router.isPreview) {
_49
return null;
_49
}
_49
return (
_49
<>
_49
<Script src={`https://www.googletagmanager.com/gtag/js?id=${TRACKING_ID}`}/>
_49
<Script id="gtag-init"dangerouslySetInnerHTML={{
_49
__html: `
_49
window.dataLayer = window.dataLayer || [];
_49
function gtag(){dataLayer.push(arguments);}
_49
gtag('js', new Date());
_49
`,
_49
}}/>
_49
</>);
_49
};
_49
export default memo(GoogleAnalytics);

This code uses the useEffect function to track user activity on the website. The first useEffect function only runs once to configure the gtag function to manually send page views.

Then, we send the page_view event. Google Analytics will still automatically send other events like user_engagement.

The second useEffect function runs every time the user changes to a new page and sends the page views.

We only show the GA scripts if there is a TRACKING_ID and the page is not in preview mode.

To implement these changes, modify the _app.tsx file.


_13
//pages/_app.tsx
_13
import "../styles/globals.css";
_13
import type { AppProps } from "next/app";
_13
import GoogleAnalytics from "../components/GoogleAnalytics";
_13
function MyApp({ Component, pageProps }: AppProps) {
_13
return (
_13
<>
_13
<GoogleAnalytics />
_13
<Component {...pageProps} />
_13
</>);
_13
}
_13
_13
export default MyApp;

This is the best option because changing the router only affects the GoogleAnalytics component and doesn't cause the whole App component to re-render, while still correctly tracking pageviews.

Stay ahead of the pack 🐶

Join the newsletter to get the latest articles and expert advice directly to your inbox.
Totally free, no spam and you can unsubscribe anytime you want!

  • Expert Tips
  • No Spam
  • Latest Updates

I'll never share any of your information with a third party. That's a promise.

Fixing cannot find name 'gtag'

To fix the type error let's add @types/gtag.js add typings when firing gtag events.


_10
yarn add @types/gtag.js
_10
#or
_10
npm install @types/gtag.js12

After this installation you should see the type error go away and hovering on gtag will show us the type definition:

599fefc5c9212ca6c6b464406.png

Conclusion

Lastly, navigate to your GA4 dashboard and check if everything works correctly. We can go to realtime report and check if all the events are firing.

That’s it! We manage to integrated Google Analytics 4 to an existing NextJS Application and add typings to gtag for more maintainability.

Thanks alot for your feedback!

The insights you share really help me with improving the quality of the content here.

If there's anything you would like to add, please send a message to:

[email protected]

Was this article this helpful?

D is for danny

About the author

Danny Engineering

A software engineer with a strong belief in human-centric design and driven by a deep empathy for users. Combining the latest technology with human values to build a better, more connected world.

Related Articles

Gobacktothetop

Made with 🐾 in 🏴󠁧󠁢󠁥󠁮󠁧󠁿

©2024 All rights reserved.