Analytics for Nuxt: Setup Guide
Nuxt is the Vue.js meta-framework, analogous to Next.js for React. Adding analytics to Nuxt requires handling both initial page loads and client-side navigation — the same challenge as other SPA frameworks.
Nuxt basics: how routing works
Nuxt (v3+) uses file-based routing in the pages/ directory. When a visitor navigates between pages, Nuxt updates the DOM without triggering a full page reload.
Like Next.js and SvelteKit, you need to hook into Nuxt's navigation lifecycle to capture every pageview, not just the initial load.
The Nuxt plugin approach
Nuxt plugins run on the client side and have access to the Nuxt router. The cleanest approach is a client-side plugin that fires a pageview on each route change.
Create plugins/analytics.client.ts (the .client suffix ensures it only runs in the browser):
// plugins/analytics.client.ts
export default defineNuxtPlugin((nuxtApp) => {
const TRACKING_ID = 'YOUR_TRACKING_ID';
const INGEST_URL = 'https://www.antlytics.com/api/ingest/pageview';
const SESSION_KEY = 'ant_sid';
function getSessionId(): string {
try {
let sid = sessionStorage.getItem(SESSION_KEY);
if (sid) return sid;
sid = crypto.randomUUID();
sessionStorage.setItem(SESSION_KEY, sid);
return sid;
} catch {
return crypto.randomUUID();
}
}
function sendPageview(pathname: string) {
fetch(INGEST_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
keepalive: true,
body: JSON.stringify({
tracking_id: TRACKING_ID,
pathname,
referrer: document.referrer || undefined,
session_id: getSessionId()
})
}).catch(() => {});
}
const router = useRouter();
// Track initial page load
nuxtApp.hook('app:mounted', () => {
sendPageview(router.currentRoute.value.path);
});
// Track subsequent navigations
router.afterEach((to) => {
sendPageview(to.path);
});
});
Replace YOUR_TRACKING_ID with your actual UUID from Settings → Tracking Snippet in your Antlytics dashboard.
JavaScript version (without TypeScript)
If you're not using TypeScript, create plugins/analytics.client.js:
// plugins/analytics.client.js
export default defineNuxtPlugin((nuxtApp) => {
const TRACKING_ID = 'YOUR_TRACKING_ID';
const INGEST_URL = 'https://www.antlytics.com/api/ingest/pageview';
const SESSION_KEY = 'ant_sid';
function getSessionId() {
try {
let sid = sessionStorage.getItem(SESSION_KEY);
if (sid) return sid;
sid = crypto.randomUUID();
sessionStorage.setItem(SESSION_KEY, sid);
return sid;
} catch {
return crypto.randomUUID();
}
}
function sendPageview(pathname) {
fetch(INGEST_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
keepalive: true,
body: JSON.stringify({
tracking_id: TRACKING_ID,
pathname,
referrer: document.referrer || undefined,
session_id: getSessionId()
})
}).catch(() => {});
}
const router = useRouter();
nuxtApp.hook('app:mounted', () => {
sendPageview(router.currentRoute.value.path);
});
router.afterEach((to) => {
sendPageview(to.path);
});
});
Verifying it works
- Start your Nuxt dev server (
npm run devorpnpm dev). - Open browser dev tools → Network tab.
- Navigate between pages.
- Look for POST requests to
api/ingest/pageviewon each navigation. - Check your Antlytics dashboard for new pageviews.
Nuxt 3 note: The
.clientplugin suffix ensures the plugin only runs on the client side. Do not remove it — the analytics code referencesdocument,sessionStorage, andfetch, none of which are available during server-side rendering.
Nuxt 3 specifics
This guide is written for Nuxt 3+ (the current stable major version). Nuxt 2 uses a different plugin format (export default function({ app }) {...}) and the app.router.afterEach approach instead of useRouter(). If you're on Nuxt 2, check the Nuxt 2 docs for the equivalent lifecycle hooks.
VERIFY_IN_REPO: If Antlytics ships a dedicated Nuxt module or npm package, check the implementation guides for updated instructions.
First-party proxy with Nuxt
To route analytics requests through your own domain (avoiding ad blocker filtering), create a Nuxt server route in server/routes/api/antlytics/pageview.post.ts:
// server/routes/api/antlytics/pageview.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const response = await fetch('https://www.antlytics.com/api/ingest/pageview', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return response.status;
});
Then update your plugin's INGEST_URL to /api/antlytics/pageview.
FAQ
Does this work with Nuxt's SSR mode?
Yes. The .client plugin runs after hydration in the browser. SSR handles the initial HTML; the plugin handles subsequent client-side analytics.
Does this work with Nuxt's static generation mode? Yes. Statically generated pages are served as HTML and hydrated by Vue in the browser. The plugin runs after hydration and tracks all navigations.
Can I use the script tag approach instead of a plugin?
Yes. Add the Antlytics script tag to your nuxt.config.ts via the head configuration:
export default defineNuxtConfig({
app: {
head: {
script: [
{ innerHTML: `/* your tracking snippet */`, type: 'text/javascript' }
]
}
}
});
The plugin approach is cleaner because it uses Nuxt's router events and avoids the pushState wrapping complexity.
Related: SPA analytics: tracking single-page apps · SvelteKit analytics · Antlytics implementation guides