# How to Implement Google Analytics in React: The Most Efficient Next.js Method for SPAs

> Implement Google Analytics in React SPAs efficiently with Next.js next/script. Learn the afterInteractive strategy for seamless, non-blocking script loading and deduplicated requests in your Next.js app.

- Repository: [Vercel/next.js](https://github.com/vercel/next.js)
- Tags: how-to-guide
- Published: 2026-02-16

---

**The most efficient way to implement Google Analytics in a React single-page application is using Next.js's `next/script` component with the `afterInteractive` strategy, which loads the script without blocking rendering while deduplicating requests across client-side navigations.**

Tracking user interactions in a React single-page application (SPA) requires careful handling of client-side route changes. The Next.js framework provides optimized primitives for loading third-party scripts, making it the ideal foundation for implementing Google Analytics React tracking without performance penalties.

## Why `next/script` Is the Optimal Solution

The `next/script` component, implemented in [`packages/next/src/client/script.tsx`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/script.tsx), provides specific optimizations that make it superior to standard HTML `<script>` tags for Google Analytics React integration.

### Automatic Script Deduplication

Unlike standard script tags that reload on every component mount, `next/script` maintains an internal cache of loaded URLs. When your SPA navigates client-side and the `GoogleAnalytics` component re-renders, the framework recognizes the existing GA library and prevents duplicate network requests.

### Loading Strategy Optimization

The `strategy` prop controls when the script executes:

- **`afterInteractive`** (recommended for GA): Loads after the page becomes interactive, ensuring critical rendering paths remain unblocked
- **`lazyOnload`**: Defers loading until after all other resources have loaded, useful for lower-priority analytics

## Implementing Google Analytics in Next.js

### Step 1: Create the Google Analytics Component

Create a wrapper component that handles script loading and initialization. This pattern mirrors the canonical implementation in [`packages/third-parties/src/google/ga.tsx`](https://github.com/vercel/next.js/blob/main/packages/third-parties/src/google/ga.tsx).

```tsx
import { useEffect } from 'react';
import Script from 'next/script';
import { useRouter } from 'next/router';

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

/**
 * Loads the GA script via next/script and tracks page views on route changes.
 */
export default function GoogleAnalytics({ measurementId }: { measurementId: string }) {
  const router = useRouter();

  // Initialise GA after the script is loaded
  const handleScriptLoad = () => {
    window.dataLayer = window.dataLayer || [];
    function gtag(...args: any[]) {
      window.dataLayer.push(args);
    }
    window.gtag = gtag;
    gtag('js', new Date());
    gtag('config', measurementId, {
      page_path: router.asPath,
    });
  };

  // Track subsequent client-side navigation
  useEffect(() => {
    const handleRouteChange = (url: string) => {
      window.gtag('event', 'page_view', { page_path: url });
    };
    router.events.on('routeChangeComplete', handleRouteChange);
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router.events]);

  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
        strategy="afterInteractive"
        onLoad={handleScriptLoad}
      />
    </>
  );
}

```

### Step 2: Register in Your Root Layout

For the Pages Router, import the component in [`pages/_app.tsx`](https://github.com/vercel/next.js/blob/main/pages/_app.tsx). For the App Router, use the root layout:

```tsx
// app/layout.tsx
import GoogleAnalytics from '@/components/GoogleAnalytics';

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

```

### Step 3: Handle Client-Side Route Changes

The `useEffect` in the component above subscribes to `router.events` on `'routeChangeComplete'`. This captures every client-side navigation in your React SPA without reloading the GA script, ensuring continuous tracking across virtual page views.

## Tracking Custom User Interactions

Once the script is loaded, you can track specific events throughout your application:

```tsx
function TrackableButton() {
  const handleClick = () => {
    window.gtag('event', 'signup_button_click', {
      category: 'engagement',
      label: 'Signup CTA',
    });
  };

  return <button onClick={handleClick}>Sign up</button>;
}

```

Since the GA library is already initialized via `next/script`, these calls are fire-and-forget and impose virtually no overhead on your React application.

## Key Source Files and Implementation Details

The Next.js repository contains several critical files that enforce and demonstrate these patterns:

- **[`packages/third-parties/src/google/ga.tsx`](https://github.com/vercel/next.js/blob/main/packages/third-parties/src/google/ga.tsx)**: The canonical reference implementation for Google Analytics in Next.js, demonstrating proper `next/script` usage and gtag initialization.

- **[`packages/eslint-plugin-next/src/rules/next-script-for-ga.ts`](https://github.com/vercel/next.js/blob/main/packages/eslint-plugin-next/src/rules/next-script-for-ga.ts)**: An ESLint rule that warns developers if they attempt to load Google Analytics incorrectly, enforcing the use of `next/script` for optimal performance.

- **[`packages/next/src/client/script.tsx`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/script.tsx)**: The core implementation of the Script component, handling deduplication logic, loading strategies (`afterInteractive`, `lazyOnload`), and the internal script cache that prevents duplicate GA loads during SPA navigation.

- **`examples/with-segment-analytics`**: Real-world examples demonstrating analytics integration patterns applicable to Google Analytics React implementations.

## Summary

- Use **`next/script`** with `strategy="afterInteractive"` to load Google Analytics without blocking the main thread or rendering path.
- Initialize **`gtag`** in the `onLoad` callback to ensure the library is available before configuration.
- Subscribe to **`router.events`** (`routeChangeComplete`) to track virtual page views during client-side navigation in your React SPA.
- Leverage the built-in **script deduplication** in [`packages/next/src/client/script.tsx`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/script.tsx) to prevent duplicate GA loads.
- Follow the canonical implementation pattern in [`packages/third-parties/src/google/ga.tsx`](https://github.com/vercel/next.js/blob/main/packages/third-parties/src/google/ga.tsx) for production-ready code.

## Frequently Asked Questions

### Why not use a standard script tag or npm package for Google Analytics React integration?

Standard HTML script tags reload on every component mount in a React SPA, causing duplicate network requests and potential double-tracking. The `next/script` component in [`packages/next/src/client/script.tsx`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/script.tsx) maintains an internal cache that deduplicates identical `src` values, ensuring the GA library loads exactly once regardless of how many times your component re-renders during client-side navigation.

### How do I prevent duplicate page view events in a Next.js App Router application?

Initialize the `gtag('config', ...)` call only once inside the `onLoad` handler of your `next/script` component, passing the initial `page_path` from `router.asPath`. Then use `useEffect` to subscribe to `router.events.on('routeChangeComplete', ...)` for subsequent navigation events. This pattern ensures the initial configuration fires once while capturing every client-side route change as a distinct page view.

### Does this implementation support Google Analytics 4 (GA4)?

Yes. The implementation described uses the standard gtag.js library loaded from `https://www.googletagmanager.com/gtag/js`, which supports both Universal Analytics and GA4. Simply use your GA4 Measurement ID (format `G-XXXXXXXXXX`) when calling `gtag('config', measurementId)`. The canonical reference in [`packages/third-parties/src/google/ga.tsx`](https://github.com/vercel/next.js/blob/main/packages/third-parties/src/google/ga.tsx) follows this exact pattern for GA4 compatibility.

### What loading strategy should I choose for performance-critical applications?

Use `strategy="afterInteractive"` (the default for external scripts) to load Google Analytics after the page becomes interactive, ensuring critical rendering paths remain unblocked. For even lower priority tracking where initial page views are less critical than core functionality, use `strategy="lazyOnload"` to defer loading until after all other resources have loaded, though this may miss early user interactions.