Analytics
The template includes two privacy-friendly analytics integrations: Umami (self-hosted or cloud) and Vercel Analytics. Both are GDPR compliant, cookie-free, and load automatically when configured.
What's included
Two client plugins:
app/plugins/umami.client.ts- Umami integrationapp/plugins/vercel-analytics.client.ts- Vercel Analytics integration
One unified composable:
app/composables/useAnalytics.ts- safetrack()that fans out to all enabled providers
Features:
- Privacy-focused (GDPR compliant, no cookies)
- Conditional loading (only loads when env vars are set)
- Automatic page view tracking
- Can use one or both simultaneously
- Unified custom event API — one call reaches every enabled provider
Umami Analytics
Quick setup
- Create an Umami account:
- Umami Cloud (recommended) or self-host
- Create a new website in your dashboard
- Copy your website ID
- Configure environment variables:
NUXT_PUBLIC_UMAMI_ID="your-website-id"
NUXT_PUBLIC_UMAMI_HOST="https://cloud.umami.is"
For self-hosted: use your own domain (e.g., https://analytics.yourdomain.com)
That's it! The plugin automatically injects the tracking script and tracks page views.
Vercel Analytics
Quick setup
- Enable in Vercel dashboard:
- Go to your project at vercel.com
- Settings → Analytics → Enable Web Analytics
- Configure environment variable:
NUXT_PUBLIC_VERCEL_ANALYTICS="true"
Using both providers
Enable both for comprehensive tracking:
NUXT_PUBLIC_UMAMI_ID="your-website-id"
NUXT_PUBLIC_UMAMI_HOST="https://cloud.umami.is"
NUXT_PUBLIC_VERCEL_ANALYTICS="true"
Why use both?
- Umami: Full control, self-hosting option, privacy compliance
- Vercel Analytics: Web Vitals, performance metrics, built-in with Vercel deployments
Environment-specific setup
Development: Omit variables to disable tracking during local development
Production: Set all desired analytics variables in your hosting platform's environment settings
Custom event tracking
Use the useAnalytics() composable to track custom events. It fans out to all enabled providers in a single call, so you don't need to know (or care) which ones are configured at any given time.
<script setup lang="ts">
const { track } = useAnalytics()
const handlePurchase = () => {
track('purchase', { plan: 'pro', amount: 29 })
}
</script>
<template>
<Button @click="handlePurchase">Buy Now</Button>
</template>
The composable lives at app/composables/useAnalytics.ts and is auto-imported by Nuxt — no manual import needed.
Why a composable instead of calling providers directly?
Calling umami.track(...) directly is unsafe: if the Umami script hasn't loaded (network failure, adblocker, missing config), referencing the bare umami identifier throws a ReferenceError that kills the surrounding handler. Optional chaining (umami?.track(...)) does not save you — it only short-circuits null/undefined values, not undeclared identifiers.
The composable handles all the safety concerns for you:
- Optional chaining on
window.umami— safe property access on an existing object (window). - Per-provider
try/catch— a failure in one provider never affects the other or the surrounding UX. import.meta.clientguard — no-op on the server, so it's safe to call from anywhere.- Dev-only error logging — silent in production to keep the console clean.
useAnalytics(). Never reference a bare umami identifier or call window.umami.track() directly without the optional chain.Privacy & compliance
GDPR compliant:
- No cookies required
- No personal data collection
- IP addresses anonymized
- Do Not Track (DNT) respected by Umami
Since these analytics are privacy-friendly, cookie consent banners typically aren't required. Check your local regulations.
Disabling in development
Both plugins automatically skip loading when environment variables aren't set. To disable analytics during local development, simply omit the variables from your .env file.
Troubleshooting
Scripts not loading:
- Verify environment variables are set correctly
- Check browser console for errors
- Check Network tab for script requests
Data not appearing:
- Wait a few minutes for data to process
- Verify website ID/configuration in dashboard
- Check that the script is loading (Network tab)