Analytics & cookie consent
Overview
The kit ships no analytics by default — nothing loads, no cookie banner appears, the base stays visually virgin. When you configure a provider, its script loads in the browser only after the user grants consent (except Plausible, which is cookieless and consent-exempt). A full consent platform — banner, granular preferences, a cookies page — comes with it.
Everything is config-driven via NEXT_PUBLIC_* env vars and a single active
web-analytics provider at a time; a Meta Pixel is available separately for the
marketing category.
Enabling a provider
Set the provider and its key (see .env.example):
# Plausible — cookieless, privacy-first, loads with NO banner
NEXT_PUBLIC_ANALYTICS_PROVIDER=plausible
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=example.com
# PostHog — product analytics, requires consent
NEXT_PUBLIC_ANALYTICS_PROVIDER=posthog
NEXT_PUBLIC_POSTHOG_KEY=phc_xxx
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
# Google Analytics 4 — requires consent
NEXT_PUBLIC_ANALYTICS_PROVIDER=ga
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
# Meta Pixel — marketing category, independent of the analytics provider
NEXT_PUBLIC_META_PIXEL_ID=1234567890
If a provider is selected without its key, the app fails fast at boot (Zod
validation in src/env.ts).
Consent model
Four cookie categories are defined in src/config/consent.config.ts:
- Necessary — always active, consent-exempt (session, CSRF, language, the consent cookie itself).
- Analytics — PostHog and GA require consent; Plausible is exempt.
- Marketing — the Meta Pixel requires consent.
- Preferences — an empty scaffold for optional personalization.
Key rules:
- Opt-in only. Nothing optional loads until the user accepts. Reject is as easy as Accept (one click), per ePrivacy anti-dark-pattern guidance.
- The banner appears only when there is something to consent to. If the only configured provider is Plausible (or nothing is configured), no banner renders. Add PostHog/GA/Meta and the relevant category appears.
- The decision is stored in a first-party
cookie_consentcookie (versioned, ~6 months). BumpconsentConfig.versionto re-prompt everyone. - GA uses Consent Mode v2 (default denied → granted) as defense in depth.
Users withdraw or change consent anytime via Cookie settings in the footer
or the /legal/cookies page.
Tracking custom events
Call track() from any client component — it dispatches to the active provider
(and the Meta Pixel if enabled) and no-ops until the relevant script is
loaded, so you never check consent yourself:
import { track } from "@/lib/analytics";
track("signup_completed", { plan: "pro" });
Pageviews on SPA navigation are handled automatically by
src/components/consent/Analytics.tsx.
CSP
Enabling a provider adds its script host to the Content-Security-Policy
script-src automatically (src/lib/security/csp.ts → analyticsScriptHosts,
wired in next.config.ts). connect-src/img-src already allow https:. This
matters only when CSP_MODE=enforce.
Extending
Provider adapters live in src/lib/analytics/providers/; the dispatch API in
src/lib/analytics/index.ts; provider selection in
src/config/analytics.config.ts; the category catalog and gating helpers in
src/config/consent.config.ts. To register a new marketing pixel, add a
technology to the catalog and an adapter, then render it (gated by its category)
in Analytics.tsx.