Skip to main content
Documentation

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. Web-analytics providers are not mutually exclusive — list more than one and they run side by side, each independently gated by its own consent category; a Meta Pixel is available separately for the marketing category.

Enabling a provider

Set NEXT_PUBLIC_ANALYTICS_PROVIDERS to a comma-separated list and fill in each listed provider's key (see .env.example):

# Plausible — cookieless, privacy-first, loads with NO banner
NEXT_PUBLIC_ANALYTICS_PROVIDERS=plausible
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=example.com

# PostHog — product analytics, requires consent
NEXT_PUBLIC_ANALYTICS_PROVIDERS=posthog
NEXT_PUBLIC_POSTHOG_KEY=phc_xxx
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com

# Google Analytics 4 — requires consent
NEXT_PUBLIC_ANALYTICS_PROVIDERS=ga
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# Both at once — perfectly valid, each still gated by its own consent category
NEXT_PUBLIC_ANALYTICS_PROVIDERS=plausible,ga
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=example.com
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# Meta Pixel — marketing category, independent of the analytics providers
NEXT_PUBLIC_META_PIXEL_ID=1234567890

If a provider is listed without its key, the app fails fast at boot (Zod validation in src/env.ts). The older NEXT_PUBLIC_ANALYTICS_PROVIDER (singular) still works as a one-provider fallback when the plural is unset, but new setups should use the plural.

Self-hosting Plausible

Set NEXT_PUBLIC_PLAUSIBLE_SRC to your own instance's script URL (e.g. https://plausible.example.com/js/script.js) to point at self-hosted Plausible CE instead of Plausible Cloud. Its origin is automatically allowlisted in the CSP script-src (analyticsScriptHosts in src/lib/security/csp.ts) — no manual CSP edit needed. Unlike PostHog/GA, Plausible's script is rendered server-side as a plain <script> (src/components/consent/PlausibleScript.tsx), not injected client-side, so it's present in the initial HTML response for any static "verify installation" checker (Plausible's own included).

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_consent cookie (versioned, ~6 months). Bump consentConfig.version to 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 every 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.tsanalyticsScriptHosts, 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.