How to Integrate Google Analytics in Next.js
Adding Google Analytics to your Next.js application is one of the quickest ways to understand how users interact with your site. It moves you from guessing about user behavior to making data-driven decisions. This guide will walk you through, step-by-step, the best way to integrate Google Analytics 4 into your Next.js project, including how to track pageviews and custom events properly.
First, Why Add Google Analytics to a Next.js App?
Modern applications built with frameworks like Next.js often operate as Single-Page Applications (SPAs). This means that after the initial page load, navigating between pages happens on the client-side without a full browser refresh. The standard Google Analytics script, by default, only tracks that initial page load. Subsequent "in-app" navigations - like clicking a link to an /about page - go completely untracked.
This is the core problem we need to solve. To get accurate user behavior data, we must manually tell Google Analytics when a user has navigated to a new page. Beyond pageviews, tracking custom events - like button clicks, form submissions, or video plays - gives you a granular view of every important interaction on your site. This lets you measure what's really driving conversions and engagement.
Step 1: Get Your GA4 Measurement ID
Before you write any code, you need a Google Analytics 4 property and its associated Measurement ID. If you already have one, feel free to skip to the next step.
If you're starting from scratch:
- Log into your Google Analytics account.
- Go to the Admin section (the gear icon in the bottom-left).
- Click "Create Account" if you're new, or "Create Property" if you're adding it to an existing account.
- Follow the setup prompts. When asked to choose a platform, select "Web."
- Enter your website URL and a name for the data stream. After creating the stream, the next screen will show you your Measurement ID.
Your Measurement ID is the most important piece of information here. It will look something like G-XXXXXXXXXX. Copy this ID and keep it handy.
Step 2: Store Your Measurement ID Securely
Hardcoding your Measurement ID directly into your application code isn't a great practice. It makes it difficult to manage different environments (like development, staging, and production). A much better approach is to use environment variables.
In the root of your Next.js project, create a new file named .env.local. Add your Measurement ID to this file:
NEXT_PUBLIC_GA_ID='G-XXXXXXXXXX'
Why NEXT_PUBLIC_?
By default, environment variables in Next.js are only available on the server. Prefixing a variable with NEXT_PUBLIC_ exposes it to the browser, which is necessary for the Google Analytics script to work on the client-side.
Step 3: Integrate the Google Analytics Script with next/script
The recommended way to add third-party scripts like Google Analytics to a Next.js app is by using the built-in next/script component. It offers built-in optimizations for loading scripts without blocking the rendering of your page.
A good practice is to create a dedicated component for your Analytics scripts to keep your main layout file clean.
Create a new file in a components directory, let's call it components/Analytics.tsx.
"use client"
import Script from 'next/script'
const GoogleAnalytics = () => {
const gaId = process.env.NEXT_PUBLIC_GA_ID
// A simple guard to ensure the script doesn't run in development
// or if the GA ID isn't provided.
if (process.env.NODE_ENV !== 'production' || !gaId) {
return null
}
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gaId}`}
/>
<Script
id="google-analytics"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [],
function gtag(){dataLayer.push(arguments),}
gtag('js', new Date()),
gtag('config', '${gaId}'),
`,
}}
/>
</>
)
}
export default GoogleAnalyticsBreaking Down the Code
- We use two
<Script>components. The first one loads the externalgtag.jslibrary from Google. The second one contains the inline JavaScript snippet that initializesgtagand configures it with your Measurement ID. - The
strategy="afterInteractive"prop tells Next.js to load this script after the page has become interactive. This is perfect for analytics scripts, as they don't need to load during the critical rendering path and won't slow down your website's initial load time. - A check (
process.env.NODE_ENV !== 'production' || !gaId) prevents the script from running in development or if the ID is missing, keeping your dev environment clean.
Step 4: Add the Analytics Component to Your Layout
Now, you need to import and use this GoogleAnalytics component in your main layout file, so it's present on every page of your site.
For the App Router (app/layout.tsx):
import GoogleAnalytics from '../components/Analytics',
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
{/* ... other code like head tag ... */}
<body>
<GoogleAnalytics />
{children}
</body>
</html>
)
}For the Pages Router (pages/_app.js):
import GoogleAnalytics from '../components/Analytics',
function MyApp({ Component, pageProps }) {
return (
<>
<GoogleAnalytics />
<Component {...pageProps} />
</>
)
}
export default MyApp,With this setup, the initial page load will be successfully tracked. But we still need to handle navigations between pages.
Step 4: Track Pageviews on Route Changes
This is the most crucial step for getting accurate analytics in a Next.js app. We need to detect when the user navigates to a new route and send a page_view event to Google Analytics.
Handling Pageviews with the App Router
Using the App Router, we can leverage client-side hooks like usePathname and useSearchParams within a useEffect to detect URL changes. Create a new client component to manage this logic and wrap your page content with it.
Create components/NavigationEvents.tsx:
'use client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
export function NavigationEvents() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
const url = `${pathname}?${searchParams.toString()}`
const gaId = process.env.NEXT_PUBLIC_GA_ID
// Send a pageview event only in production
if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined' && gaId) {
window.gtag('config', gaId, {
page_path: url,
})
}
// You can also add other analytics tracking here
}, [pathname, searchParams])
return null
}Now, import this component into your app/layout.tsx, wrap your children with Suspense, and include NavigationEvents:
import { Suspense } from 'react'
import { NavigationEvents } from '../components/NavigationEvents'
export default function RootLayout({ children }) {
// ...
return (
<html lang="en">
<body>
{/* ... your GA script component here ... */}
{children}
<Suspense fallback={null}>
<NavigationEvents />
</Suspense>
</body>
</html>
)
}Using <Suspense> here is a good practice as it ensures that hooks like useSearchParams can be used without causing rendering waterfalls.
Handling Pageviews with the Pages Router
The Pages Router has a simpler, built-in event system that makes this straightforward. Listen to routeChangeComplete events in your pages/_app.js:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// ... other imports
const handleRouteChange = (url) => {
const gaId = process.env.NEXT_PUBLIC_GA_ID
if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined' && gaId) {
window.gtag('config', gaId, {
page_path: url,
})
}
}
function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<>
<GoogleAnalytics />
<Component {...pageProps} />
</>
)
}
export default MyApp,This adds an event listener when the app mounts and cleans it up on unmount. Now every in-app navigation triggers a page_view.
Step 5: How to Track Custom Events
Once you're tracking pageviews, the next step is to track specific user interactions. This could be anything from signing up for a newsletter to clicking a "Download" button.
Create a helper function to make this easy. In lib/gtag.js:
export const event = ({ action, category, label, value }) => {
const gaId = process.env.NEXT_PUBLIC_GA_ID
if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined' && gaId) {
window.gtag('event', action, {
event_category: category,
event_label: label,
value: value,
})
}
}Now, you can import and use this helper in your components. For example, on a button:
import * as gtag from '../lib/gtag'
const ShareButton = ({ platform }) => {
const handleShare = () => {
// Other sharing logic...
gtag.event({
action: `share_${platform}`,
category: 'Social Share',
label: 'User shared content on ' + platform,
})
}
return (
<button onClick={handleShare}>
Share on {platform}
</button>
)
}Step 6: Verify Your Analytics Are Working
After implementing all of these steps, verify that everything is functioning correctly.
- Realtime Reports: Open your website in one tab and go to Reports > Realtime in Google Analytics. You should see yourself active and new
page_viewevents appear. - Developer Tools: Use your browser's developer tools, go to the "Network" tab, filter for "google-analytics.com", and observe pings on page load and route changes.
- Tag Assistant Legacy Extension: Google offers a Chrome extension Tag Assistant Legacy to help debug tags.
Final Thoughts
Integrating Google Analytics into Next.js is quite straightforward, but it requires tracking route changes to account for its SPA nature. Using next/script for optimal script loading, combined with listening to route changes, you get a reliable and accurate picture of user engagement.
Once you have this data, you can analyze user behavior and optimize your site accordingly. At Graphed, we make connecting your data sources like Google Analytics, Shopify, and ad platforms simple—allowing you to ask questions and build dashboards in plain language. Try Graphed to turn your new data into actionable decisions in seconds, not hours.
Related Articles
How to Connect Facebook to Google Data Studio: The Complete Guide for 2026
Connecting Facebook Ads to Google Data Studio (now called Looker Studio) has become essential for digital marketers who want to create comprehensive, visually appealing reports that go beyond the basic analytics provided by Facebook's native Ads Manager. If you're struggling with fragmented reporting across multiple platforms or spending too much time manually exporting data, this guide will show you exactly how to streamline your Facebook advertising analytics.
Appsflyer vs Mixpanel: Complete 2026 Comparison Guide
The difference between AppsFlyer and Mixpanel isn't just about features—it's about understanding two fundamentally different approaches to data that can make or break your growth strategy. One tracks how users find you, the other reveals what they do once they arrive. Most companies need insights from both worlds, but knowing where to start can save you months of implementation headaches and thousands in wasted budget.
DashThis vs AgencyAnalytics: The Ultimate Comparison Guide for Marketing Agencies
When it comes to choosing the right marketing reporting platform, agencies often find themselves torn between two industry leaders: DashThis and AgencyAnalytics. Both platforms promise to streamline reporting, save time, and impress clients with stunning visualizations. But which one truly delivers on these promises?