Google Analytics 4 with Next.js and TypeScript

Alan Yang
Alan Yang
February 9, 2022 - 4 min read

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.


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.


  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);
    };'routeChangeComplete', handleRouteChange);
    return () => {'routeChangeComplete', handleRouteChange);
  }, []);

  return (
      {isProduction && (
          <Script src={`${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,
      <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 (
        <input type='text' onChange={(event) => setSearchTerm(} />
        <button onClick={() => handleOnClick()}>Search</button>

export default SearchPage;