Skip to main content
Back to Blog
Tutorials
3 min read
November 12, 2024

How to Set Up Google Analytics 4 on a Next.js Website

Install Google Analytics 4 on your Next.js App Router site the right way. Covers the Script component, consent management, event tracking, and privacy compliance.

Ryel Banfield

Founder & Lead Developer

Google Analytics 4 (GA4) provides traffic and user behavior data. Setting it up in a Next.js App Router application requires care to avoid performance impact and privacy compliance issues.

Step 1: Get Your Measurement ID

  1. Go to Google Analytics (analytics.google.com)
  2. Create a new property or select an existing one
  3. Go to Admin > Data Streams > Web
  4. Copy your Measurement ID (starts with G-)

Step 2: Add the Script

Option A: Using @next/third-parties (Recommended)

pnpm add @next/third-parties
// app/layout.tsx
import { GoogleAnalytics } from "@next/third-parties/google";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleAnalytics gaId="G-XXXXXXXXXX" />
    </html>
  );
}

This is the simplest approach. The @next/third-parties package loads GA4 with optimal performance settings.

Option B: Using next/script

// app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', 'G-XXXXXXXXXX');
        `}
      </Script>
    </html>
  );
}

strategy="afterInteractive" loads the script after the page becomes interactive, avoiding impact on LCP and FCP.

Step 3: Track Page Views in App Router

GA4 with gtag.js automatically tracks page views for traditional multi-page navigations. For client-side navigations in Next.js App Router, you need additional tracking:

// components/analytics/AnalyticsProvider.tsx
"use client";

import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";

declare global {
  interface Window {
    gtag: (...args: unknown[]) => void;
  }
}

export function AnalyticsProvider() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (typeof window.gtag !== "undefined") {
      const url = pathname + (searchParams?.toString() ? `?${searchParams.toString()}` : "");
      window.gtag("config", "G-XXXXXXXXXX", {
        page_path: url,
      });
    }
  }, [pathname, searchParams]);

  return null;
}

Add this to your layout:

import { Suspense } from "react";
import { AnalyticsProvider } from "@/components/analytics/AnalyticsProvider";

// In your layout body:
<Suspense fallback={null}>
  <AnalyticsProvider />
</Suspense>

The Suspense boundary is required because useSearchParams needs it in Next.js App Router.

Step 4: Custom Event Tracking

Track specific user interactions:

// lib/analytics.ts
export function trackEvent(eventName: string, parameters?: Record<string, string | number>) {
  if (typeof window !== "undefined" && typeof window.gtag !== "undefined") {
    window.gtag("event", eventName, parameters);
  }
}

Usage:

import { trackEvent } from "@/lib/analytics";

// Track a button click
<button onClick={() => {
  trackEvent("cta_click", { location: "hero", variant: "primary" });
}}>
  Get Started
</button>

// Track a form submission
function handleSubmit() {
  trackEvent("form_submit", { form_name: "contact" });
}

// Track a download
<a
  href="/whitepaper.pdf"
  onClick={() => trackEvent("file_download", { file_name: "whitepaper.pdf" })}
>
  Download Whitepaper
</a>

Step 5: Consent Management

For GDPR/CCPA compliance, do not load GA4 until the user consents:

// components/analytics/ConsentBanner.tsx
"use client";

import { useState, useEffect } from "react";

export function ConsentBanner() {
  const [showBanner, setShowBanner] = useState(false);

  useEffect(() => {
    const consent = localStorage.getItem("analytics-consent");
    if (consent === null) {
      setShowBanner(true);
    }
  }, []);

  function handleAccept() {
    localStorage.setItem("analytics-consent", "granted");
    setShowBanner(false);
    // Initialize GA4
    window.gtag("consent", "update", {
      analytics_storage: "granted",
    });
  }

  function handleDecline() {
    localStorage.setItem("analytics-consent", "denied");
    setShowBanner(false);
  }

  if (!showBanner) return null;

  return (
    <div className="fixed bottom-0 left-0 right-0 bg-white p-4 shadow-lg dark:bg-gray-900">
      <p>We use cookies to analyze site traffic. Accept to help us improve.</p>
      <div className="mt-2 flex gap-2">
        <button onClick={handleAccept} className="rounded bg-blue-600 px-4 py-2 text-white">
          Accept
        </button>
        <button onClick={handleDecline} className="rounded border px-4 py-2">
          Decline
        </button>
      </div>
    </div>
  );
}

Set default consent to denied:

<Script id="consent-defaults" strategy="beforeInteractive">
  {`
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('consent', 'default', {
      analytics_storage: 'denied',
    });
  `}
</Script>

Environment Variables

Store your measurement ID in environment variables:

# .env.local
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID!} />

Only load in production:

{process.env.NODE_ENV === "production" && (
  <GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID!} />
)}

Verification

  1. Open your site in Chrome
  2. Open DevTools > Network tab
  3. Filter for "google" or "gtag"
  4. Verify the script loads
  5. Navigate between pages and check that page_view events fire
  6. Go to GA4 Realtime report to see live data

Need Analytics Help?

We set up analytics, track conversions, and build custom dashboards for our clients. Contact us for analytics setup and optimization.

Google AnalyticsGA4Next.jsanalyticstutorial

Ready to Start Your Project?

RCB Software builds world-class websites and applications for businesses worldwide.

Get in Touch

Related Articles