How to Fix PayloadCMS Tracking Events Not Firing | OpsBlu Docs

How to Fix PayloadCMS Tracking Events Not Firing

Fix GA4, GTM, and pixel events not firing on PayloadCMS — React/Next.js SSR hydration, client component boundaries, and API data layer timing debugging

Common issues and solutions for tracking problems on PayloadCMS frontends (React/Next.js).

Common Causes

  1. Client-side rendering issues - Tracking code runs before DOM ready
  2. Server-side rendering conflicts - Accessing window during SSR
  3. Environment variables - Missing or incorrect tracking IDs
  4. Ad blockers - Browser extensions blocking tracking
  5. Timing issues - Events firing before trackers initialize

Diagnostic Steps

Step 1: Check Browser Console

Open DevTools (F12) and look for:

For GA4:

// Check if gtag is loaded
typeof window.gtag !== 'undefined'

// View dataLayer
console.log(window.dataLayer);

For Meta Pixel:

// Check if fbq is loaded
typeof window.fbq !== 'undefined'

// Check pixel status
window.fbq('getState');

For GTM:

// Check if dataLayer exists
console.log(window.dataLayer);

// Check GTM loaded
console.log(window.google_tag_manager);

Step 2: Check Network Tab

  1. Open DevTools > Network
  2. Filter by "analytics", "facebook", or "gtm"
  3. Reload page
  4. Verify tracking requests are sent

Step 3: Use Browser Extensions


Issue: Tracking Not Loading

Symptom

No tracking requests in Network tab, undefined in console.

Causes & Solutions

Missing Environment Variables

Check .env.local:

# Ensure these are set
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
NEXT_PUBLIC_META_PIXEL_ID=123456789
NEXT_PUBLIC_GTM_ID=GTM-XXXXXXX

Verify in code:

console.log('GA ID:', process.env.NEXT_PUBLIC_GA_ID);
console.log('Pixel ID:', process.env.NEXT_PUBLIC_META_PIXEL_ID);
console.log('GTM ID:', process.env.NEXT_PUBLIC_GTM_ID);

Note: Environment variables must start with NEXT_PUBLIC_ to be accessible in the browser.

Script Not Loaded

Check Script component usage:

// Correct
import Script from 'next/script';

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

// Wrong
<script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>

Ensure script in correct location:

  • Pages Router: pages/_app.js
  • App Router: app/layout.js

Issue: SSR Errors

Symptom

Error: window is not defined or document is not defined

Solution: Check for Client-Side Only Code

Wrap in useEffect:

import { useEffect } from 'react';

export default function MyComponent() {
  useEffect(() => {
    // Safe - runs only on client
    if (window.gtag) {
      window.gtag('event', 'page_view');
    }
  }, []);

  return <div>Content</div>;
}

Or use typeof check:

if (typeof window !== 'undefined' && window.gtag) {
  window.gtag('event', 'page_view');
}

Issue: Events Fire on Some Pages, Not Others

Symptom

Tracking works on homepage but not on dynamic routes.

Solution: Ensure Event Tracking in Each Page

Check dynamic pages have tracking:

// pages/blog/[slug].js
import { useEffect } from 'react';

export default function BlogPost({ post }) {
  useEffect(() => {
    // Track page view
    if (window.gtag) {
      window.gtag('event', 'page_view', {
        page_title: post.title,
        page_location: window.location.href,
      });
    }
  }, [post]);

  return <article>{post.title}</article>;
}

Issue: Duplicate Events

Symptom

Same event fires twice in analytics.

Causes & Solutions

Multiple Implementations

Check for:

  • Script in _app.js AND _document.js
  • GA4 installed AND GTM with GA4 tag
  • Both native implementation AND package

Remove duplicates:

// Choose ONE method:
// Option 1: Direct implementation
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXX" />

// Option 2: GTM (includes GA4 tag)
<Script src="https://www.googletagmanager.com/gtm.js?id=GTM-XXX" />

// NOT BOTH

React StrictMode

In development, React StrictMode causes double rendering.

This is expected in development:

// pages/_app.js
import React from 'react';

export default function MyApp({ Component, pageProps }) {
  return (
    <React.StrictMode> {/* Causes double renders in dev */}
      <Component {...pageProps} />
    </React.StrictMode>
  );
}

Not an issue in production.


Issue: Events Fire in Development, Not Production

Symptom

Tracking works on localhost, fails on live site.

Causes & Solutions

Environment Variable Not Set in Production

For Vercel:

  1. Go to Project Settings > Environment Variables
  2. Add NEXT_PUBLIC_GA_ID etc.
  3. Redeploy

For Netlify:

  1. Site Settings > Build & Deploy > Environment
  2. Add variables
  3. Trigger new deploy

Build Cache Issue

# Clear Next.js cache and rebuild
rm -rf .next
npm run build

Issue: GTM Not Firing Tags

Symptom

GTM loads, but tags don't fire.

Diagnostic Steps

Check Preview Mode

  1. In GTM, click Preview
  2. Enter your site URL
  3. Verify:
    • Container loads
    • Triggers activate
    • Tags fire

Check Triggers

Ensure trigger conditions met:

// If trigger is "Custom Event = pageview"
// Ensure you're pushing the event:
window.dataLayer.push({
  event: 'pageview', // Must match exactly
  page: window.location.pathname,
});

Check Variables

Verify data layer variables populate:

// In console:
window.dataLayer.filter(item => item.event === 'yourEvent');

Issue: Ad Blockers Blocking Tracking

Symptom

Tracking works in incognito, not in normal browsing.

Solution

Ad blockers prevent tracking - this is expected behavior.

For testing:

  1. Disable ad blocker
  2. Use incognito mode
  3. Test on mobile device

For users:


Issue: Events Fire Too Early

Symptom

Events fire before tracker initialized.

Solution: Wait for Tracker

For GA4:

function trackEvent(eventName, params) {
  // Wait for gtag to be defined
  if (typeof window.gtag === 'undefined') {
    setTimeout(() => trackEvent(eventName, params), 100);
    return;
  }

  window.gtag('event', eventName, params);
}

For Meta Pixel:

function trackPixelEvent(eventName, params) {
  if (typeof window.fbq === 'undefined') {
    setTimeout(() => trackPixelEvent(eventName, params), 100);
    return;
  }

  window.fbq('track', eventName, params);
}

Better: Use callback:

// pages/_app.js
const [analyticsReady, setAnalyticsReady] = useState(false);

useEffect(() => {
  // Wait for scripts to load
  const checkAnalytics = setInterval(() => {
    if (window.gtag && window.fbq) {
      setAnalyticsReady(true);
      clearInterval(checkAnalytics);
    }
  }, 100);

  return () => clearInterval(checkAnalytics);
}, []);

Issue: Events Fire But Don't Appear in Reports

Symptom

Events show in DebugView but not in standard reports.

Solutions

GA4 Real-Time vs Standard Reports

  • Real-Time: Instant
  • Standard Reports: 24-48 hour delay

Check Real-Time first:

  1. GA4 > Reports > Realtime
  2. Perform action on site
  3. Verify event appears

Event Name Formatting

GA4 event names:

  • Must be lowercase with underscores
  • Max 40 characters
  • No spaces
// Correct
gtag('event', 'form_submission');

// Wrong
gtag('event', 'Form Submission'); // Spaces not allowed
gtag('event', 'formSubmission'); // CamelCase not recommended

Issue: Events Not Tracking After API Calls

Check async/await:

const handleSubmit = async (e) => {
  e.preventDefault();

  try {
    const response = await fetch(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/contact`, {
      method: 'POST',
      body: JSON.stringify(formData),
    });

    // Track AFTER successful response
    if (response.ok) {
      window.gtag('event', 'form_submission', {
        form_name: 'contact',
      });
    }
  } catch (error) {
    console.error('Error:', error);
    // Track error
    window.gtag('event', 'form_error', {
      error_message: error.message,
    });
  }
};

Best Practices for Reliable Tracking

1. Initialize Trackers Early

// In _app.js, before any components
useEffect(() => {
  // Initialize all trackers
  if (window.gtag) {
    window.gtag('config', process.env.NEXT_PUBLIC_GA_ID);
  }
  if (window.fbq) {
    window.fbq('init', process.env.NEXT_PUBLIC_META_PIXEL_ID);
  }
}, []);

2. Use Defensive Coding

// Always check if tracker exists
if (typeof window !== 'undefined' && window.gtag) {
  window.gtag('event', 'my_event');
}

3. Log Events in Development

function trackEvent(eventName, params) {
  if (process.env.NODE_ENV === 'development') {
    console.log('Tracking:', eventName, params);
  }

  if (window.gtag) {
    window.gtag('event', eventName, params);
  }
}

Next Steps


Additional Resources