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
Method 1: Next.js with Prismic (Recommended)
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
- Navigate to Reports > Realtime in GA4
- Visit your Prismic-powered site
- 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:
- Configure Prismic Event Tracking for Slices and user interactions
- Set up GTM for Prismic for advanced tracking
- Optimize LCP for Prismic Images
- Troubleshoot Events Not Firing