Installing Google Analytics 4 on Prismic with Next.js | OpsBlu Docs

Installing Google Analytics 4 on Prismic with Next.js

Complete guide to setting up GA4 tracking on Prismic-powered sites using Next.js, Gatsby, or custom React implementations

Prismic is a headless CMS that requires client-side implementation of GA4 tracking. This guide covers GA4 integration for Next.js, Gatsby, and custom React implementations using Prismic.


Prerequisites

Before you begin:

  • Have a Google Analytics 4 property created
  • Know your GA4 Measurement ID (format: G-XXXXXXXXXX)
  • Have a Prismic repository set up
  • Be using Next.js, Gatsby, or another JavaScript framework

Step 1: Install Dependencies

For Next.js 13+ (App Router):

npm install @prismicio/client @prismicio/next @prismicio/react

Step 2: Create GA4 Component

Create components/GoogleAnalytics.js:

'use client';

import Script from 'next/script';

export default function GoogleAnalytics({ measurementId }) {
  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${measurementId}', {
            page_path: window.location.pathname,
          });
        `}
      </Script>
    </>
  );
}

Step 3: Add to Root Layout

In app/layout.js (Next.js 13+ App Router):

import { PrismicPreview } from '@prismicio/next';
import { repositoryName } from '@/prismicio';
import GoogleAnalytics from '@/components/GoogleAnalytics';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        <GoogleAnalytics measurementId="G-XXXXXXXXXX" />
      </head>
      <body>
        {children}
        <PrismicPreview repositoryName={repositoryName} />
      </body>
    </html>
  );
}

Step 4: Handle Client-Side Navigation

For Next.js App Router, create app/analytics.js:

'use client';

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

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

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

  return null;
}

Include in app/layout.js:

import { Analytics } from './analytics';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  );
}

For Next.js Pages Router

In pages/_app.js:

import Script from 'next/script';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { PrismicProvider } from '@prismicio/react';
import Link from 'next/link';

const GA_MEASUREMENT_ID = 'G-XXXXXXXXXX';

function MyApp({ Component, pageProps }) {
  const router = useRouter();

  useEffect(() => {
    const handleRouteChange = (url) => {
      if (typeof window.gtag !== 'undefined') {
        window.gtag('config', GA_MEASUREMENT_ID, {
          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=${GA_MEASUREMENT_ID}`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${GA_MEASUREMENT_ID}', {
            page_path: window.location.pathname,
          });
        `}
      </Script>
      <PrismicProvider internalLinkComponent={(props) => <Link {...props} />}>
        <Component {...pageProps} />
      </PrismicProvider>
    </>
  );
}

export default MyApp;

Method 2: Gatsby with Prismic

Step 1: Install Gatsby GA4 Plugin

npm install gatsby-plugin-google-gtag gatsby-source-prismic

Step 2: Configure gatsby-config.js

module.exports = {
  plugins: [
    {
      resolve: 'gatsby-source-prismic',
      options: {
        repositoryName: 'your-repo-name',
        accessToken: process.env.PRISMIC_ACCESS_TOKEN,
        schemas: {
          // Your custom type schemas
        },
      },
    },
    {
      resolve: 'gatsby-plugin-google-gtag',
      options: {
        trackingIds: [
          'G-XXXXXXXXXX', // GA4 Measurement ID
        ],
        gtagConfig: {
          anonymize_ip: true,
          cookie_expires: 0,
        },
        pluginConfig: {
          head: false, // Put script in <head>
          respectDNT: true,
          exclude: ['/preview/**', '/admin/**'],
        },
      },
    },
  ],
};

Step 3: Track Prismic Preview Events

Create src/components/PrismicPreviewHandler.js:

import { useEffect } from 'react';
import { usePrismicPreviewContext } from 'gatsby-source-prismic';

export default function PrismicPreviewHandler() {
  const [previewData] = usePrismicPreviewContext();

  useEffect(() => {
    if (previewData && typeof window.gtag !== 'undefined') {
      window.gtag('event', 'prismic_preview_accessed', {
        event_category: 'preview',
        event_label: 'Prismic Preview Mode',
      });
    }
  }, [previewData]);

  return null;
}

Step 4: Environment Variables

Create .env.development and .env.production:

PRISMIC_REPOSITORY_NAME=your-repo-name
PRISMIC_ACCESS_TOKEN=your-access-token

Method 3: Custom React Implementation

For vanilla React or other frameworks:

Install Prismic Client

npm install @prismicio/client @prismicio/react

Create GA4 Hook

Create hooks/useAnalytics.js:

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

export function useAnalytics(measurementId) {
  const location = useLocation();

  useEffect(() => {
    // Load gtag script
    const script = document.createElement('script');
    script.async = true;
    script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`;
    document.head.appendChild(script);

    // Initialize gtag
    window.dataLayer = window.dataLayer || [];
    function gtag() {
      window.dataLayer.push(arguments);
    }
    window.gtag = gtag;
    gtag('js', new Date());
    gtag('config', measurementId);
  }, [measurementId]);

  useEffect(() => {
    if (typeof window.gtag !== 'undefined') {
      window.gtag('config', measurementId, {
        page_path: location.pathname + location.search,
      });
    }
  }, [location, measurementId]);
}

Use in App Component

import { useAnalytics } from './hooks/useAnalytics';
import { PrismicProvider } from '@prismicio/react';
import { client } from './prismic';

function App() {
  useAnalytics('G-XXXXXXXXXX');

  return (
    <PrismicProvider client={client}>
      {/* Your app components */}
    </PrismicProvider>
  );
}

export default App;

Prismic-Specific Considerations

Tracking Prismic Preview Mode

Prismic's preview feature requires special handling:

// In your preview handler or component
import { usePrismicPreview } from '@prismicio/react';

export function PreviewHandler() {
  const [isPreview] = usePrismicPreview();

  useEffect(() => {
    if (isPreview && typeof window.gtag !== 'undefined') {
      // Set custom dimension for preview mode
      window.gtag('set', 'user_properties', {
        prismic_preview: 'true',
      });

      // Track preview access
      window.gtag('event', 'preview_mode_active', {
        event_category: 'prismic',
        event_label: 'Preview Mode',
      });
    }
  }, [isPreview]);

  return null;
}

Tracking Prismic Content Types

When rendering Prismic documents:

import { PrismicRichText } from '@prismicio/react';

export function PrismicPage({ document }) {
  useEffect(() => {
    if (document && typeof window.gtag !== 'undefined') {
      window.gtag('event', 'page_view', {
        page_title: document.data.title,
        content_type: document.type,
        prismic_id: document.id,
        prismic_uid: document.uid,
        last_publication_date: document.last_publication_date,
      });
    }
  }, [document]);

  return (
    <div>
      <h1>{document.data.title}</h1>
      <PrismicRichText field={document.data.content} />
    </div>
  );
}

Environment-Specific Configuration

Development vs. Production

Use environment variables to control GA4:

Next.js - next.config.js:

module.exports = {
  env: {
    GA_MEASUREMENT_ID: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID,
  },
};

.env.local (development):

NEXT_PUBLIC_GA_MEASUREMENT_ID=G-DEV-XXXXXXXXXX

.env.production:

NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

Component with environment check:

export default function GoogleAnalytics() {
  const measurementId = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;
  const isDevelopment = process.env.NODE_ENV === 'development';

  if (!measurementId || isDevelopment) {
    return null; // Don't load GA4 in development
  }

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

Validation & Testing

1. Real-Time Reports

  1. Navigate to Reports > Realtime in GA4
  2. Visit your Prismic-powered site
  3. Confirm page views appear within 30 seconds

2. DebugView

Enable debug mode during development:

gtag('config', 'G-XXXXXXXXXX', {
  'debug_mode': true
});

Then check Admin > DebugView in GA4 to see events in real-time.

3. Browser Console

Test GA4 is loaded:

// Open browser console on your site
console.log(window.gtag);
console.log(window.dataLayer);

Should output the gtag function and dataLayer array.

4. Google Tag Assistant

Install Tag Assistant and verify:

  • GA4 tag is detected
  • Configuration is correct
  • Events are firing

Common Issues & Solutions

Issue: GA4 Not Loading in Next.js

Cause: Next.js Script component not loading correctly

Solution: Ensure strategy="afterInteractive" is set:

<Script
  src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
  strategy="afterInteractive" // Critical!
/>

Issue: Client-Side Navigation Not Tracked

Cause: SPA navigation doesn't trigger page views

Solution: Add route change listener (see Next.js example above)

Issue: Prismic Preview Pages Not Tracked

Cause: Preview pages may be excluded

Solution: Ensure preview routes aren't in exclude list:

// Gatsby example
pluginConfig: {
  exclude: ['/admin/**'], // Don't exclude /preview
}

Issue: Duplicate Page Views

Cause: GA4 loaded multiple times

Solution: Ensure GA4 component only rendered once in root layout


Performance Optimization

Use Next.js Script Component

The Next.js Script component automatically optimizes loading:

import Script from 'next/script';

// Uses afterInteractive strategy
<Script
  src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"
  strategy="afterInteractive"
/>

Benefits:

  • Defers loading until page is interactive
  • Doesn't block initial page render
  • Optimizes Core Web Vitals

Lazy Load for Gatsby

For Gatsby, ensure plugin loads asynchronously:

{
  resolve: 'gatsby-plugin-google-gtag',
  options: {
    pluginConfig: {
      head: false, // Load in body, not head
    },
  },
}

Next Steps

Now that GA4 is installed:


Additional Resources