Google Analytics 4 with Next.js and TypeScript
In this article, we will walk through how to add Google Analytics 4 to your Next.js app with TypeScript. Google Analytics is a free service that helps users measure traffic, engagement, and conversions on their website or app.
Table of Contents
Initial Setup
Add your Google Analytics 4 ID to your environmental variables. The ID will be in the form of G-XXXXXXXXXX
. If your ID starts with UA
, you are using the Universal Analytics ID. To set up a Google Analytics 4 property, you can refer to Google's migration instructions.
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-XXXXXXXXXX
To use gtag in your TypeScript app, you will need to add @types/gtag.js
as a dependency.
yarn add -D @types/gtag.js
Lib Functions
Create a lib
folder in your root folder or src folder and create analytics.ts
within it. These lib functions will be used to track page views and user events, and because we added the @types/gtag.js
dependency, we can use the gtag
function without any errors.
const GOOGLE_ANALYTICS_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID;
if (!GOOGLE_ANALYTICS_ID) {
throw new Error('Missing Google Analytics environment variable');
}
const event = (action: Gtag.EventNames, params: Gtag.EventParams) => {
window.gtag('event', action, params);
};
const pageView = (url: URL) => {
window.gtag('config', GOOGLE_ANALYTICS_ID, {
page_path: url,
});
};
export { event, GOOGLE_ANALYTICS_ID, pageView };
Log Page Views
To log page views, we need to use the useRouter
and useEffect
hooks to listen for the routeChangeComplete
event because we need to know when the user has finished navigating to a new page.
To prevent logging page views during development, we use process.env.NODE_ENV
to check if we are in production mode.
import type { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import Script from 'next/script';
import { useEffect } from 'react';
import * as gtag from '@/lib/analytics';
const isProduction = process.env.NODE_ENV === 'production';
const MyApp = ({ Component, pageProps }: AppProps) => {
const router = useRouter();
useEffect(() => {
const handleRouteChange = (url: URL) => {
if (isProduction) gtag.pageView(url);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return (
<>
{isProduction && (
<>
<Script src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GOOGLE_ANALYTICS_ID}`} />
<Script id='gtag-init'>
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gtag.GOOGLE_ANALYTICS_ID}', {
page_path: window.location.pathname,
});
`}
</Script>
</>
)}
<Component {...pageProps} />
</>
);
};
export default MyApp;
It takes between 24-48 hours for data to begin appearing in your Google Analytics dashboard, but you can look at Realtime Overview in the dashboard to see if your page views are being logged.
Log Specific Events
You can use the event function from @/lib/analytics
to log specific user actions within your app. You can see the different types of user actions that you can log by looking at the Google Analytics 4 Event Tracking documentation or by referring to the EventNames
type from gtag.
type EventNames =
| 'add_payment_info'
| 'add_to_cart'
| 'add_to_wishlist'
| 'begin_checkout'
| 'checkout_progress'
| 'exception'
| 'generate_lead'
| 'login'
| 'page_view'
| 'purchase'
| 'refund'
| 'remove_from_cart'
| 'screen_view'
| 'search'
| 'select_content'
| 'set_checkout_option'
| 'share'
| 'sign_up'
| 'timing_complete'
| 'view_item'
| 'view_item_list'
| 'view_promotion'
| 'view_search_results';
Search Event Example
Below is an example of how to log a search by users.
import { useState } from 'react';
import * as gtag from '@/lib/analytics';
const SearchPage = () => {
const [searchTerm, setSearchTerm] = useState('');
const handleOnClick = () => {
gtag.event('search', {
search_term: searchTerm,
});
// Do other things...
};
return (
<div>
<div>
<input type='text' onChange={(event) => setSearchTerm(event.target.value)} />
</div>
<div>
<button onClick={() => handleOnClick()}>Search</button>
</div>
</div>
);
};
export default SearchPage;