Skip to main content
Localizia

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).

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 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.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.