Events not tracking correctly on Storyblok sites can result from framework-specific issues, Visual Editor isolation, or configuration errors. This guide helps diagnose and fix common tracking issues.
Common Symptoms
- GA4 events don't appear in DebugView
- Meta Pixel Helper shows no pixel detected
- GTM Preview Mode shows no tags firing
- Events fire in development but not production
- Events don't fire in Visual Editor
- Events fire on initial load but not on navigation
Diagnostic Steps
Step 1: Verify Tracking Scripts Load
// Check if GA4 loaded
console.log(typeof window.gtag); // Should output 'function'
// Check if Meta Pixel loaded
console.log(typeof window.fbq); // Should output 'function'
// Check if GTM loaded
console.log(window.dataLayer); // Should output an array
Step 2: Check Network Requests
- Open DevTools → Network tab
- Filter by:
analytics,gtag,fbevents, orgtm - Reload page
- Verify tracking requests are sent
Common Issues & Solutions
Issue 1: Events Not Firing on Client-Side Navigation
Symptoms:
- Events fire on initial page load
- Events don't fire when navigating between pages
Cause: SPA navigation not tracked
Solution for Nuxt 3:
// plugins/page-view-tracking.client.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:finish', () => {
// Track GA4 page view
if (typeof window.gtag !== 'undefined') {
window.gtag('config', 'G-XXXXXXXXXX', {
page_path: window.location.pathname,
});
}
// Track Meta Pixel page view
if (typeof window.fbq !== 'undefined') {
window.fbq('track', 'PageView');
}
// Track GTM page view
if (typeof window.dataLayer !== 'undefined') {
window.dataLayer.push({
event: 'pageview',
page: window.location.pathname,
});
}
});
});
Issue 2: Scripts Not Loading in Visual Editor
Symptoms:
- Tracking works on published site
- No tracking in Visual Editor
- Visual Editor iframe isolation
Cause: Visual Editor runs in iframe, isolating tracking scripts
Solution:
// plugins/storyblok-editor-tracking.client.ts
export default defineNuxtPlugin(() => {
const route = useRoute();
// Check if in Visual Editor
const isEditor = computed(() => route.query._storyblok !== undefined);
// Only track if NOT in iframe or if parent window tracking is available
const canTrack = window === window.parent || window.parent.gtag;
if (canTrack || !isEditor.value) {
// Safe to track
if (typeof window.gtag !== 'undefined') {
window.gtag('event', 'page_view');
}
}
});
Issue 3: Environment Variables Not Set
Symptoms:
- Tracking works in development
- No tracking in production build
Cause: Environment variables not configured
Solution:
Check .env:
# Development
NUXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
NUXT_PUBLIC_META_PIXEL_ID=1234567890123456
NUXT_PUBLIC_GTM_ID=GTM-XXXXXXX
STORYBLOK_TOKEN=your-token
# Production (use same or separate)
Verify in code:
// plugins/gtag.client.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig();
const measurementId = config.public.gaMeasurementId;
console.log('GA Measurement ID:', measurementId); // Debug
if (!measurementId) {
console.error('GA4 Measurement ID not found!');
return;
}
// Load GA4
});
Issue 4: Data Layer Not Populating
Symptoms:
- GTM Preview shows empty data layer
- Variables show
undefined
Cause: Data layer pushed before GTM loads
Solution:
// Ensure dataLayer initializes before GTM
export default defineNuxtConfig({
app: {
head: {
script: [
{
innerHTML: 'window.dataLayer = window.dataLayer || [];',
type: 'text/javascript',
},
],
},
},
});
Verify data layer pushes:
// Debug data layer
export default defineNuxtPlugin(() => {
const pushStoryData = (story: any) => {
console.log('Pushing to dataLayer:', {
event: 'storyblok_data_ready',
storyblok: { id: story.id },
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'storyblok_data_ready',
storyblok: { id: story.id, component: story.content.component },
});
console.log('dataLayer after push:', window.dataLayer);
};
return { provide: { pushStoryData } };
});
Issue 5: Duplicate Events
Symptoms:
- Event count higher than expected
- Multiple pixel fires
Cause: Multiple tracking scripts or re-renders
Solution:
// Use ref to prevent duplicates
export const useComponentTracking = (blok: any) => {
const hasTracked = ref(false);
const track = () => {
if (hasTracked.value) return; // Prevent duplicates
hasTracked.value = true;
if (typeof window.gtag !== 'undefined') {
window.gtag('event', 'component_view', {
component_type: blok.component,
});
}
};
onMounted(track);
return { track };
};
Issue 6: Component Events Not Tracking
Symptoms:
- Page views work
- Component interactions don't track
- Intersection Observer not firing
Cause: Components not wrapped with tracking
Solution:
<!-- components/TrackedComponent.vue -->
<template>
<div ref="elementRef" v-editable="blok">
<slot />
</div>
</template>
<script setup>
const { blok } = defineProps(['blok']);
const elementRef = ref<HTMLElement | null>(null);
const hasTracked = ref(false);
onMounted(() => {
if (!elementRef.value || hasTracked.value) return;
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !hasTracked.value) {
hasTracked.value = true;
console.log('Component viewed:', blok.component); // Debug
if (typeof window.gtag !== 'undefined') {
window.gtag('event', 'view_item', {
event_category: 'component',
event_label: blok.component,
});
}
}
});
},
{ threshold: 0.5 }
);
observer.observe(elementRef.value);
onUnmounted(() => observer.disconnect());
});
</script>
Issue 7: Ad Blockers Blocking Tracking
Symptoms:
- Tracking works in incognito mode
- Works without browser extensions
Cause: Ad blocker extensions blocking analytics
Solution:
Test without ad blockers:
- Open incognito/private mode
- Disable extensions
- Test tracking
Consider server-side tracking for Nuxt:
// server/api/track.post.ts
export default defineEventHandler(async (event) => {
const { eventType, eventData } = await readBody(event);
// Send to GA4 Measurement Protocol
const measurementId = process.env.GA_MEASUREMENT_ID;
const apiSecret = process.env.GA_API_SECRET;
await $fetch(
`https://www.google-analytics.com/mp/collect?measurement_id=${measurementId}&api_secret=${apiSecret}`,
{
method: 'POST',
body: {
client_id: eventData.clientId,
events: [{ name: eventType, params: eventData }],
},
}
);
return { success: true };
});
Debugging Checklist
- Verify tracking scripts load (check console)
- Check Network tab for analytics requests
- Enable debug mode (GA4 DebugView, Meta Pixel Helper, GTM Preview)
- Test in incognito mode without extensions
- Verify environment variables are set
- Check for client-side navigation tracking
- Ensure data layer initializes before GTM
- Look for duplicate script loads
- Test on production build
- Check if Visual Editor isolation is the issue
- Verify component tracking is implemented
- Test with different browsers
Testing Tools
Browser Console Testing
// Test GA4
gtag('event', 'test_event', { test: true });
// Test Meta Pixel
fbq('track', 'Lead', { test: true });
// Test GTM
window.dataLayer.push({ event: 'test_event' });