Skip to content
BoringStack
Star

Cookie consent

2 min read

BoringStack ships a custom-built cookie consent banner in the UI template, not a third-party widget. It’s ~3 KB of code, sends nothing externally, and is fully lint-gated.

Third-party banners (cookiebot, iubenda, OneTrust, …) are surprisingly heavyweight: 300–700 KB of blocking JS, external network calls before consent is granted (itself a regulatory grey area), per-domain accounts to set up, and zero ability for your stack’s lint rules to catch a mis-wired analytics call.

A 100-line zustand store + a banner component does the same job at ~2 KB without sending anything to a third party, and the lint plugin can refuse to merge a posthog.capture(...) outside a useIsConsentCategoryEnabled('analytics') gate.

Three categories, persisted in localStorage under bs.cookie-consent.v1:

CategoryDefaultWhat it covers
essentialalways on (locked)Auth, CSRF, session, language preference. The app can’t sign you in without these.
analyticsoffFirst-party product analytics (page views, feature usage). Never sent to a third party without explicit consent.
marketingoffCross-site identifiers used by ad / campaign tooling. Most forks won’t need this category, strip the row in CookieConsentBanner.tsx if so.

The store also persists configuredAt, the ISO timestamp of the choice, useful for compliance audits (“when did this user consent?”). The key is versioned (.v1) so a future model change re-prompts existing users instead of silently inheriting a stale answer.

import { useIsConsentCategoryEnabled } from "@/features/consent";
function AnalyticsHook() {
const analyticsEnabled = useIsConsentCategoryEnabled("analytics");
useEffect(() => {
if (!analyticsEnabled) {
return;
}
// load your analytics here
}, [analyticsEnabled]);
return null;
}

useIsConsentCategoryEnabled returns false until the user has explicitly configured consent, opt-in, not opt-out, so callers can safely gate analytics / marketing scripts on it without leaking before the banner is dismissed.

Surface a “Cookie preferences” entry in your settings menu that calls useCookieConsentStore.getState().reset(), the next page render shows the banner again with the previous categories pre-selected.

  • apps/ui/src/features/consent/CookieConsent.store.ts, zustand store with persist middleware
  • apps/ui/src/features/consent/CookieConsent.types.ts, category definitions
  • apps/ui/src/features/consent/CookieConsentBanner/, banner + modal components, hooks, and tests
  • apps/ui/src/app/App.tsx, banner mounted at the root