Screenshot collage showing Nuxt + Google Analytics 4 (GA4) integration with code editor, browser dev tools, and GA4 real-time dashboard.

Nuxt Google Analytics 4 Fix: How We Solved the Tracking Issue

If you’re setting up Nuxt Google Analytics 4 and find that tracking isn’t working, you’re not alone. Recently, we ran into this exact issue on afitpilot.com — pageviews weren’t showing, no g/collect requests were firing, and GA was basically dead in the water.

Here’s the full breakdown of what went wrong, how we fixed it, and the lessons worth remembering for Nuxt Google Analytics 4.


The Problem

Even though the GA Measurement ID was set correctly in nuxt.config.ts, Analytics was failing completely in production.

Google Analytics 4 realtime dashboard showing no active users, indicating broken Nuxt GA4 tracking.
Afitpilot website in Chrome DevTools Network tab with no GA4 collect requests, highlighting the issue.

Symptoms:

  • No <script> tags for gtag.js in the HTML source
  • No inline GA initialization script
  • No g/collect requests in the Network tab
  • GA dashboard showed zero activity

At first glance, everything looked fine — GA ID was present, GTM noscript iframe was loaded — but tracking just wasn’t happening for Nuxt Google Analytics 4.


The Root Cause

The issue came down to script loading order and timing.

Our analytics plugin (analytics-unified.client.ts) was:

  1. Injecting the GA script client-side with document.createElement('script')
  2. Calling gtag() before the library had finished loading
  3. Never outputting the GA scripts during server-side rendering (SSR)

This created a race condition: GA was called before it was ready. As a result, no tracking initialized.


The Fix

The Nuxt Google Analytics 4 solution was surprisingly simple: move Analytics initialization into app.vue using Nuxt’s useHead() composable.

Nuxt project code diff showing fixed GA4 implementation using useHead in app.vue
VS Code editor showing Nuxt app.vue file with corrected Google Analytics 4 initialization code

Correct Implementation in app.vue

<script setup>
const config = useRuntimeConfig()
const gaId = config.public.googleAnalyticsId

useHead({
  script: gaId && gaId !== 'G-XXXXXXXXXX' ? [
    {
      children: `window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', '${gaId}', {
        send_page_view: true,
        anonymize_ip: true,
        cookie_flags: 'SameSite=None;Secure'
      });`,
      type: 'text/javascript'
    },
    {
      src: `https://www.googletagmanager.com/gtag/js?id=${gaId}`,
      async: true
    }
  ] : [],
  link: [
    { rel: 'preconnect', href: 'https://www.googletagmanager.com' },
    { rel: 'preconnect', href: 'https://www.google-analytics.com' }
  ]
})
</script>

Why This Works

Google Analytics 4 realtime dashboard confirming active user tracking for Afitpilot.
Afitpilot website with Chrome DevTools showing successful Google Analytics 4 request to gtag.js

SSR Injection: Scripts are rendered in the initial HTML, not added after hydration.
Correct Load Order: Inline gtag() definition comes before the external script loads.
Immediate Execution: GA is ready as soon as the page loads.
Async Script: gtag.js loads non-blocking but still finds the pre-populated dataLayer.


Verifying the Fix

After deploying, here’s how we confirmed everything was working:

  1. View Source → GA inline + external scripts appear in HTML
  2. Network Tabg/collect requests firing
  3. Consolewindow.dataLayer populated
  4. GA Dashboard → Real-time users now showing

Key Lessons

  • Do this: use useHead() in app.vue for critical scripts
  • Don’t do this: inject GA via client-only plugins with DOM manipulation
  • Order matters: define gtag() and dataLayer before loading gtag.js
  • Test in production: GA often fails silently unless you check HTML + network traffic

Final Take

Google Analytics is notoriously picky about how it’s initialized. In our case, fixing the load order and moving initialization to SSR was the difference between no tracking at all and a fully functioning GA setup.

If you’re struggling with GA not firing in your Nuxt app, start by checking your HTML source. If the scripts aren’t there, chances are you’re injecting them too late.

For more of my dev fixes, see:

For deep thinkers, creators, and curious minds. One post. Zero noise.

We don’t spam! Read our privacy policy for more info.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *