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.
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:
Fill-in the details and click Create stream:
After that you should see the Web stream details below and get the Measurement ID:
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._10NEXT_PUBLIC_GA4_TRACKING_ID="G-0PMW35MR3C"_10123
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
.
_20import { Html, Head, Main, NextScript } from "next/document";_20_20export default function Document() {_20 return (_20 <Html>_20 <Head />_20 <body>_20 <Main />_20 <NextScript />_20 <script_20dangerouslySetInnerHTML={{_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_49import { useRouter } from "next/router";_49import Script from "next/script";_49import { memo, useEffect } from "react";_49const TRACKING_ID = process.env.NEXT_PUBLIC_GA4_TRACKING_ID!;_49const GoogleAnalytics = () => {_49 const router = useRouter();_49_49useEffect(() => {_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 }, []);_49useEffect(() => {_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};_49export 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_13import "../styles/globals.css";_13import type { AppProps } from "next/app";_13import GoogleAnalytics from "../components/GoogleAnalytics";_13function MyApp({ Component, pageProps }: AppProps) {_13 return (_13 <>_13 <GoogleAnalytics />_13 <Component {...pageProps} />_13 </>);_13}_13_13export 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
.
Fixing cannot find name 'gtag'
To fix the type error let's add @types/gtag.js
add typings when firing gtag events.
_10yarn add @types/gtag.js_10#or_10npm 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:
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?
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.