Reference
Analytics
Vexell ships a privacy-first analytics scaffold: vendor-agnostic, consent-gated, GDPR/CCPA-ready out of the box. Nothing fires before the user accepts cookies.
Consent first by default. The CookieConsent banner mounts globally. If the user hasn't accepted, track() is a no-op (history is still kept locally for the dev panel).
Track an event
Pick your edition. The interface is symmetric — only the call site differs.
// 1. From any client component:
'use client'
import { useAnalytics } from '@/lib/analytics'
export function CTAButton() {
const analytics = useAnalytics()
return (
<button
onClick={() => analytics.track('cta_clicked', {
label: 'hero',
location: 'home',
})}
>
Start free
</button>
)
}
// 2. Typed event catalog at lib/analytics/events.ts:
import type { AnalyticsEvent } from '@/lib/analytics/events'
// AnalyticsEvent unions Marketing + App + Security event names.
// Misspelled event names fail at compile time.
// 3. Inspect from devtools:
// window.vexellAnalytics.consented
// window.vexellAnalytics.historyCross-tab consent sync
Both editions persist consent to localStorage and dispatch a vexell:consent-changed CustomEvent. Listening to both gives you cross-tab + same-tab coverage.
// CookieConsent persists to localStorage AND dispatches a CustomEvent
// so cross-tab AND same-tab listeners stay in sync.
//
// localStorage events fire across tabs; the custom event covers same-tab.
// AnalyticsProvider listens to both and load/unloads its vendor.
window.dispatchEvent(new CustomEvent('vexell:consent-changed', {
detail: { analytics: true, marketing: true, preferences: true }
}))
localStorage.setItem('vexell.consent.v1', JSON.stringify({
analytics: true, marketing: true, preferences: true,
timestamp: Date.now(),
}))PostHog adapter sample
Drop in any vendor. The provider exposes a load(consent) / track(name, payload) / unload() interface. Below is a working PostHog adapter; GA4, Plausible, and Segment follow the same shape.
// lib/analytics/adapters/posthog.ts (Next.js — same shape works for HTML)
import posthog from 'posthog-js'
export const posthogAdapter = {
load(consent) {
if (!consent.analytics) return
posthog.init(process.env.NEXT_PUBLIC_ANALYTICS_KEY, {
api_host: process.env.NEXT_PUBLIC_ANALYTICS_HOST,
capture_pageview: false, // we'll fire it ourselves
autocapture: false, // privacy-first default
disable_session_recording: true, // turn on only with explicit consent
})
},
track(name, payload) {
posthog.capture(name, payload)
},
identify(userId, traits) {
posthog.identify(userId, traits)
},
unload() {
posthog.reset()
},
}Read next
- ApiKeyManager — manage vendor API keys via the dashboard demo.