Implementing Google Analytics in a Next.js SSR Application
Preface
This article covers:
- Setting up Google Analytics (GA4) in a Next.js application with Server-Side Rendering (SSR).
- Using
next/script
to dynamically load Google Analytics for performance optimisation. - Tracking page views correctly with Next.js'
usePathname
hook. - Ensuring GA runs only in production.
Solution
Google Analytics is essential for tracking user behaviour in a Next.js application. Since Next.js supports SSR and static generation, we need a proper setup to ensure accurate tracking across different page navigation methods.
1. Adding Google Analytics Scripts
To integrate GA, we use Next.js’ next/script
component. This ensures the script loads efficiently without blocking rendering.
import Script from 'next/script';
export default function GoogleAnalytics() {
if (process.env.NODE_ENV !== 'production') return null;
return (
<>
<Script
async
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_ID}`}
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.NEXT_PUBLIC_GA_ID}', {
page_path: window.location.pathname,
});
`}
</Script>
</>
);
}
strategy="afterInteractive"
ensures the script loads after the page becomes interactive.- GA will not load during development (
NODE_ENV !== 'production'
). - It sends an initial page view when the script loads.
2. Tracking Page Views on Route Changes
Next.js uses client-side navigation via the next/navigation
package. We need to detect route changes and manually send page view events.
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
declare global {
interface Window {
gtag?: (command: 'event' | 'config' | 'set', eventName: string, eventParams?: Record<string, unknown>) => void;
}
}
export default function TrackPageView() {
const pathname = usePathname();
useEffect(() => {
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('event', 'page_view', {
page_path: pathname,
});
}
}, [pathname]);
return null;
}
- Why
usePathname()
? It gives the current route, even when navigating between pages using Next.js' built-in router. - Ensures that every page navigation is sent to GA. Without this, only the first page load would be tracked.
3. Including Analytics in _app.js
or Root Layout
Since Next.js 13+ supports the app
directory, analytics should be loaded inside layout.tsx
:
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<GoogleAnalytics />
<TrackPageView />
{children}
</body>
</html>
);
}
This ensures that:
- Google Analytics scripts are injected once.
- Page views are tracked for each route change.
Why?
-
Efficient Loading with
next/script
- Loads Google Analytics asynchronously.
- Prevents blocking page rendering.
-
Accurate Tracking
- GA is triggered on route changes (
usePathname
). - Ensures event tracking beyond initial page load.
- GA is triggered on route changes (
-
Production-Only Execution
- GA scripts do not run during development.
- Avoids unnecessary noise in analytics.
-
Future-Proof Implementation
- Works with Next.js App Router.
- Can be expanded for event tracking (e.g., button clicks).
Summary
By integrating Google Analytics correctly in a Next.js SSR app, we ensure that all pages are tracked effectively. This method is optimised for performance, accurate route tracking, and minimal impact on development.
My Technical Skills

AWS

JavaScript

TypeScript

React

Next.js

Cypress

Figma
