← Back to Blog
vuejsprivacycookielessinstallationdeveloper guide

Vue.js Analytics Without Cookies: Privacy-First Tracking for Vue Apps

Add cookieless, privacy-first analytics to Vue.js SPAs. Auto-tracks route changes, hash navigation, and custom events.

EngageTrack Team··8 min read

Vue.js single-page applications need analytics that understand client-side routing. Traditional analytics tools like GA4 were built for multi-page websites where every navigation triggers a full page load and a new HTTP request. In a Vue SPA, the initial page load is the only "real" page load — everything after that is JavaScript manipulating the DOM and updating the URL via history.pushState or hash changes.

If your analytics tool does not detect these client-side navigations, you see one pageview per session regardless of how many pages the visitor actually viewed. That makes your analytics useless for understanding user behavior.

EngageTrack provides vuejs analytics privacy by default: cookieless tracking that auto-detects Vue Router navigations (both history and hash mode), requires no consent banner, and works with a single script tag. No Vue plugin needed.

Why Do Vue.js SPAs Need Special Analytics Handling?

When a visitor navigates in a Vue.js SPA, the browser does not make a new HTTP request to the server. Vue Router updates the URL using the History API (history.pushState) or hash changes (#/about#/pricing), renders the new component, and the browser stays on the same HTML document.

Analytics tools that rely on the traditional page load model — listening for window.onload or tracking HTTP requests — miss these navigations entirely. GA4 handles this through its gtag('config') call with page_path updates, but it requires manual configuration for SPAs and still carries the 45KB script and cookie overhead.

EngageTrack listens for both pushState changes and hashchange events. When Vue Router navigates to a new route, EngageTrack detects it and fires a pageview event automatically. No Vue plugin, no router middleware, no manual event calls.

How Do You Install EngageTrack in a Vue.js Application?

The simplest approach. Add the script tag to your index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Vue App</title>
    <script
      defer
      data-site-id="YOUR_SITE_ID"
      src="https://cdn.engagetrack.net/sdk.js"
    ></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

This works for Vue 3 projects created with Vite (create-vue), Vue CLI, or any other build tool.

Method 2: Dynamic Loading in App.vue

If you prefer to load the script programmatically:

<!-- App.vue -->
<script setup lang="ts">
import { onMounted } from "vue";
 
onMounted(() => {
  if (!document.querySelector("[data-site-id]")) {
    const script = document.createElement("script");
    script.defer = true;
    script.dataset.siteId = "YOUR_SITE_ID";
    script.src = "https://cdn.engagetrack.net/sdk.js";
    document.head.appendChild(script);
  }
});
</script>
 
<template>
  <RouterView />
</template>

Both methods produce the same result. Method 1 is simpler and recommended for most projects.

For the complete setup reference, see the Vue.js installation guide.

How Does EngageTrack Handle Vue Router Navigation?

EngageTrack auto-detects client-side navigation by listening for:

  1. history.pushState and history.replaceState — Used by Vue Router in history mode (createWebHistory())
  2. popstate events — Fired when users click the browser back/forward buttons
  3. hashchange events — Used by Vue Router in hash mode (createWebHashHistory())

When any of these events fire, EngageTrack captures the new URL and sends a pageview event.

History Mode (Default)

Vue Router's default mode uses createWebHistory(), which maps to clean URLs like /about, /pricing, /dashboard. EngageTrack tracks these navigations automatically with no additional configuration.

// router/index.ts
import { createRouter, createWebHistory } from "vue-router";
 
const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: "/", component: () => import("../views/Home.vue") },
    { path: "/pricing", component: () => import("../views/Pricing.vue") },
    { path: "/dashboard", component: () => import("../views/Dashboard.vue") },
  ],
});
 
export default router;

No changes needed. EngageTrack tracks every route change.

Hash Mode

If your Vue Router uses hash mode (createWebHashHistory()), add the data-hash="true" attribute to enable hash change tracking:

<script
  defer
  data-site-id="YOUR_SITE_ID"
  data-hash="true"
  src="https://cdn.engagetrack.net/sdk.js"
></script>

This tells EngageTrack to listen for hashchange events and treat URL hash changes as page navigations. Without this attribute, hash changes are ignored (since many sites use hashes for anchor links, not navigation).

For full details on script configuration options, see the script configuration docs.

How Do You Track Custom Events in Vue Components?

Use the global window.engagetrack object to track custom events from any Vue component:

<!-- components/PricingCard.vue -->
<script setup lang="ts">
interface Props {
  plan: string;
  price: string;
}
 
const props = defineProps<Props>();
 
function handleSelectPlan() {
  window.engagetrack?.track("plan_selected", {
    plan: props.plan,
    price: props.price,
  });
}
</script>
 
<template>
  <div class="pricing-card">
    <h3>{{ plan }}</h3>
    <p>{{ price }}/month</p>
    <button @click="handleSelectPlan">Choose {{ plan }}</button>
  </div>
</template>

For TypeScript projects, declare the global type:

// src/types/engagetrack.d.ts
declare global {
  interface Window {
    engagetrack?: {
      track: (event: string, props?: Record<string, string>) => void;
      identify: (userId: string, traits?: Record<string, string>) => void;
      getVisitorId: () => string;
    };
  }
}
 
export {};

Common Events for Vue SPA Analytics

// Track signup
window.engagetrack?.track("signup", { method: "email" });
 
// Track feature usage
window.engagetrack?.track("feature_used", { feature: "export_csv" });
 
// Track trial start
window.engagetrack?.track("trial_started", { plan: "pro" });
 
// Track error boundary hits
window.engagetrack?.track("error_occurred", { page: route.path });

How Do You Identify Users After Login?

After a user authenticates in your Vue app, you can link their anonymous visitor session to their account using the identify API:

<!-- composables/useAuth.ts -->
<script setup lang="ts">
import { useRouter } from "vue-router";
 
const router = useRouter();
 
async function handleLogin(email: string, password: string) {
  const response = await fetch("/api/auth/login", {
    method: "POST",
    body: JSON.stringify({ email, password }),
  });
 
  const user = await response.json();
 
  // Identify the visitor with their user ID
  window.engagetrack?.identify(user.id, {
    plan: user.plan,
    role: user.role,
  });
 
  router.push("/dashboard");
}
</script>

The identify call links the current anonymous visitor session to the provided user ID. This enables tracking the complete journey from first anonymous visit through signup and conversion. No PII (email, name) is stored in EngageTrack — only the user ID you provide and any non-PII traits.

How Does EngageTrack Compare to Other Vue.js Analytics Options?

FeatureEngageTrackGA4Plausible
SPA route trackingAuto (pushState + hash)Requires manual configAuto (pushState)
Hash mode supportYes (data-hash="true")Requires manual eventsLimited
Script size (gzipped)3KB45KB+~1KB
CookiesNoneMultiple (2yr)None
GDPR compliant (no banner)YesNoYes
Vue plugin neededNoNo (but recommended)No
Custom eventsYesYesYes
Visitor identificationYesYesNo
Revenue attributionStripe, LemonSqueezy, Paddle, PolarGoogle Ads onlyNo
Conversion funnelsYesYesBasic
REST APIFull read/writeReporting APIRead-only
EU data hostingFrankfurtOptionalEU
Starting price€5/moFree (with limits)$9/mo

Can You Use EngageTrack with Nuxt.js?

Yes. Nuxt.js is built on Vue and supports the same script tag approach. In Nuxt 3, add the script to your nuxt.config.ts:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          defer: true,
          "data-site-id": "YOUR_SITE_ID",
          src: "https://cdn.engagetrack.net/sdk.js",
        },
      ],
    },
  },
});

Or add it to your app.vue layout:

<!-- app.vue -->
<template>
  <NuxtPage />
</template>
 
<script setup lang="ts">
useHead({
  script: [
    {
      defer: true,
      "data-site-id": "YOUR_SITE_ID",
      src: "https://cdn.engagetrack.net/sdk.js",
    },
  ],
});
</script>

EngageTrack tracks Nuxt client-side navigations (via Vue Router under the hood) automatically.

FAQ

Does EngageTrack track Vue Router navigation automatically?

Yes. EngageTrack listens for history.pushState, history.replaceState, and popstate events, which Vue Router triggers on every navigation in history mode. For hash mode, add data-hash="true" to the script tag. No Vue plugin, router guard, or manual tracking code is needed.

What about hash-based routing in Vue?

Add the data-hash="true" attribute to the EngageTrack script tag. This enables hashchange event listening, so navigations like #/about#/pricing are tracked as separate pageviews. Without this attribute, hash changes are not tracked (to avoid false positives from anchor links on non-SPA sites).

Can I use EngageTrack with Nuxt.js?

Yes. Nuxt.js uses Vue Router internally, so EngageTrack's automatic route detection works in Nuxt applications. Add the script tag via nuxt.config.ts or useHead() in your app layout. Both Nuxt SSR and static generation modes are supported — the tracking script runs in the browser after hydration.

Does EngageTrack work with Vue's keep-alive component?

Yes. <keep-alive> caches component instances but still triggers Vue Router navigation events. EngageTrack tracks the route change regardless of whether the target component was freshly mounted or restored from cache.

Do I need a Vue plugin or composable for EngageTrack?

No. EngageTrack is a standalone script that attaches to window.engagetrack. You access it via window.engagetrack?.track() from any component. If you prefer a more Vue-idiomatic approach, you can create a thin composable wrapper, but it is not required and not provided by EngageTrack — the global API is the intended interface.


Vue.js SPAs need analytics that understand client-side routing, and EngageTrack handles it automatically — history mode, hash mode, and Nuxt.js included. No cookies, no consent banner, no Vue plugin to install. Start your free 14-day trial and have Vue analytics running in under a minute. No credit card required.

Related Articles