[opencookies]sub-4kb · pre-1.0
A headless consent state machine.
Tiny core. Adapters for every major framework. A Vite plugin that yells at you when a script sets a cookie behind a category the user hasn’t accepted yet.
App.tsx
import {
OpenCookiesProvider,
ConsentGate,
} from "@opencookies/react";
const config = {
categories: [
{ key: "essential", label: "Essential", locked: true },
{ key: "analytics", label: "Analytics" },
{ key: "marketing", label: "Marketing" },
],
};
export function App() {
return (
<OpenCookiesProvider config={config}>
<YourApp />
<ConsentGate requires="analytics">
<GoogleAnalytics />
</ConsentGate>
</OpenCookiesProvider>
);
}[01]what it does
Just the consent layer. Nothing else.
- Headless core
- A state machine that tracks categories, persists choices, and emits events. The UI is whatever you build.
- Framework adapters
- First-class hooks for React, Vue, Solid, Svelte, and Angular. Same store, same events, framework-idiomatic API.
- Vite plugin
- Watches for cookie writes during dev. Throws if a script sets a cookie behind a category the user hasn’t accepted.
- Static scanner
- CI step that scans built bundles for ungated cookie usage so things don’t regress between releases.
- Integrations
- GA, Meta Pixel, GTM, Hotjar, PostHog — load them gated behind the right consent category by default.
- CLI (planned)
- Bootstrap a config from your existing cookies, audit a deployed site, generate a per-environment policy.
[01]
[02]
[03]
[04]
[05]
[06]
[02]dev plugin
Catch leaky cookies before users do.
The Vite plugin patches document.cookie in dev and refuses writes that fall outside the categories the user has accepted — with a stack trace pointing at the line that did it.
vite dev — terminal
! consent violation
[opencookies] ungated cookie write blocked
cookie: _ga
category: analytics (not accepted)
source: src/lib/analytics.ts:18:5
fix: guard with consent.has("analytics")[03]install
Two lines and you’re shipping.
bash
pnpm add @opencookies/core @opencookies/reactCookieBanner.tsx
import { useConsent } from "@opencookies/react";
export function CookieBanner() {
const { acceptAll, acceptNecessary } = useConsent();
return (
<div>
<button onClick={acceptAll}>Accept all</button>
<button onClick={acceptNecessary}>Necessary only</button>
</div>
);
}