Google Analytics for VitePress
Add GDPR-compliant Google Analytics to your VitePress site with one line of code.
One-Line Setup
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme';
import { enhanceWithConsent } from '@structured-world/vue-privacy/vitepress';
export default enhanceWithConsent(DefaultTheme, {
gaId: 'G-XXXXXXXXXX', // Your GA4 measurement ID
});TypeScript Users
Add vue-router as a dev dependency for type resolution: npm i -D vue-router
This is only needed for TypeScript compilation — vue-router is not used at runtime (VitePress has its own router).
That's it. This automatically:
- Loads
gtag.jswith Google Consent Mode v2 defaults - Detects EU users and shows a consent banner when required
- Tracks page views on every VitePress navigation (SPA-aware)
- Stores consent in a cookie for 365 days
How SPA Tracking Works
VitePress is a single-page app — navigating between pages doesn't trigger a full page reload. Standard Google Analytics misses these navigations.
enhanceWithConsent handles this automatically:
- Sets
send_page_view: falseto prevent duplicate page views on init - Tracks the initial page view after initialization (skipped if analytics consent was denied)
- Watches VitePress router for every navigation
- Sends
page_viewevents with correctpage_pathandpage_titleafter DOM updates
You don't need to write any router watching code.
Frontmatter Events
Fire GA4 events automatically when users visit specific pages using frontmatter:
---
# docs/pricing.md
ga4Title: Pricing Page
ga4Event:
name: view_pricing
params:
page_type: pricing
---
# Pricing
Choose your plan...---
# docs/signup-success.md
ga4Title: Registration Complete
ga4Event:
name: sign_up
params:
method: docs
---
# Welcome!
Thank you for signing up.Supported frontmatter fields:
| Field | Type | Description |
|---|---|---|
ga4Title | string | Custom page title for page_view event |
ga4Event | { name, params? } | GA4 event to fire on page view |
Events fire automatically when the user navigates to the page. This is useful for:
- Tracking conversions (sign up pages, thank you pages)
- Measuring engagement with specific content
- A/B testing page variations
Custom Theme with Layout Slots
If you need to add components to VitePress layout slots alongside the consent banner:
// docs/.vitepress/theme/index.ts
import { h } from 'vue';
import DefaultTheme from 'vitepress/theme';
import type { Theme } from 'vitepress';
import { enhanceWithConsent } from '@structured-world/vue-privacy/vitepress';
const consentTheme = enhanceWithConsent(DefaultTheme, {
gaId: 'G-XXXXXXXXXX',
});
export default {
...consentTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
// Replace with your own component
'layout-bottom': () => h('div', 'Custom footer content'),
});
},
} satisfies Theme;Manual Setup (Advanced)
If you need full control over the consent plugin:
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme';
import { createConsentPlugin, ConsentBanner } from '@structured-world/vue-privacy/vue';
import type { Theme } from 'vitepress';
import { h } from 'vue';
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.use(createConsentPlugin({
gaId: 'G-XXXXXXXXXX',
sendPageView: false, // Important: disable for SPA
}));
},
Layout() {
return h(DefaultTheme.Layout, null, {
'layout-bottom': () => h(ConsentBanner),
});
},
} satisfies Theme;SPA Page Tracking
When using manual setup, you'll need to implement your own router watching for SPA page tracking. The enhanceWithConsent function does this automatically.
Skip Initial Tracking
enhanceWithConsent always tracks the initial page view. If you need to skip it (e.g., for custom analytics logic), use the manual setup and implement your own tracking with useConsent().trackPageView().
Configuration
All standard options are supported:
enhanceWithConsent(DefaultTheme, {
gaId: 'G-XXXXXXXXXX',
euDetection: 'auto', // 'auto' | 'cloudflare' | 'api' | 'always' | 'never'
banner: {
title: 'Cookie Preferences',
message: 'This site uses cookies for analytics.',
acceptAll: 'Accept',
rejectAll: 'Decline',
privacyLink: '/privacy',
},
cookie: {
name: 'docs_consent',
expiry: 365,
},
});Event Tracking
Track custom events in your VitePress components:
<!-- docs/components/DownloadButton.vue -->
<script setup>
import { useConsent } from '@structured-world/vue-privacy/vue';
const { trackEvent } = useConsent();
function onDownload(file: string) {
trackEvent('file_download', {
file_name: file,
file_extension: file.split('.').pop(),
});
}
</script>
<template>
<button @click="onDownload('guide.pdf')">Download PDF</button>
</template>Lead Generation
Track documentation-specific conversions:
<script setup>
import { useConsent } from '@structured-world/vue-privacy/vue';
const { trackGenerateLead, trackSignUp } = useConsent();
// Newsletter signup
function onSubscribe(email: string) {
trackGenerateLead({ lead_source: 'docs_newsletter' });
}
// Account creation
function onCreateAccount() {
trackSignUp('docs');
}
</script>See Ecommerce Tracking for more event types.
Styling
The banner inherits VitePress CSS variables when possible. Override with:
/* docs/.vitepress/theme/custom.css */
:root {
--consent-bg: var(--vp-c-bg);
--consent-text: var(--vp-c-text-1);
--consent-btn-accept-bg: var(--vp-c-brand);
}Import in your theme:
import './custom.css';