A lightweight React library for managing cookies and consent—zero dependencies, zero bloat.
Warning
Using this library does not automatically make your application GDPR-compliant. It is your responsibility as a developer to implement a compliant cookie consent banner that meets the requirements of applicable data protection laws.
import { CookieProvider, useConsent, useCookie, useCookieBanner } from "@jocraft2010/clonkie";
const SCOPES = [{ scope: "analytics" }, { scope: "marketing" }];
function App() {
return (
<CookieProvider scopes={SCOPES}>
<Page />
<Banner />
</CookieProvider>
);
}
function Page() {
const analytics = useConsent("analytics");
const [userId, setUserId] = useCookie<string>("user_id", "essential");
return (
<div>
<p>Analytics: {analytics ? "on" : "off"}</p>
<input value={userId ?? ""} onChange={(e) => setUserId(e.target.value)} />
</div>
);
}
function Banner() {
const banner = useCookieBanner();
if (!banner.isOpen) return null;
return (
<div>
<button onClick={banner.acceptAll}>Accept</button>
<button onClick={banner.rejectAll}>Reject</button>
</div>
);
}npm install @jocraft2010/clonkie
yarn add @jocraft2010/clonkie
pnpm add @jocraft2010/clonkie
bun add @jocraft2010/clonkiePeer dependencies: react ^18.0.0 || ^19.0.0. Make sure these are installed in your project.
Wraps your app with a consent context. Automatically injects an "essential" scope if not provided.
Props:
| Prop | Type | Required |
|---|---|---|
scopes |
CookieScope[] |
Yes |
children |
ReactNode |
Yes |
| Field | Type | Default |
|---|---|---|
scope |
string |
— |
path |
string |
"/" |
maxAge |
number |
31536000 |
domain |
string |
current domain |
secure |
boolean |
true |
sameSite |
"strict" | "lax" | "none" |
"lax" |
function useConsent(scope: string): boolean;Returns true if the given scope is consented. The "essential" scope always returns true.
function useCookie<T>(name: string, scope: string): [T | undefined, (value: T) => boolean];Returns a tuple of the current cookie value and a setter. The setter respects consent — if consent for the scope is denied, the cookie is deleted and the setter returns false (no-op).
function useCookieBanner(): {
isOpen: boolean;
setBannerOpen: (open: boolean) => void;
consent: Record<string, boolean>;
setConsent: (consent: Record<string, boolean>) => void;
acceptAll: () => void;
rejectAll: () => void;
applySelection: () => void;
};Draft-based consent UI hook. Changes made via setConsent, acceptAll, or rejectAll are held in local state and only applied to the context when applySelection() is called.
function useOnAcceptAll(fn: () => void): void;Registers a side-effect callback that fires when all scopes are accepted.
function useOnRejectAll(fn: () => void): void;Registers a side-effect callback that fires when all non-essential scopes are rejected.
function useOnConsentChange(fn: (consent: Record<string, boolean>) => void): void;Subscribes to any consent state change. The callback receives the full consent map.
import { useConsent } from "clonkie";
import { useEffect } from "react";
function Analytics() {
const consented = useConsent("analytics");
useEffect(() => {
if (consented) {
// load gtag / GA script
}
}, [consented]);
return null;
}const [theme, setTheme] = useCookie<"light" | "dark">("theme", "essential");Since "essential" consent is always granted, the cookie is always available.
import { useOnConsentChange } from "clonkie";
function ConsentLogger() {
useOnConsentChange((consent) => {
localStorage.setItem("consent", JSON.stringify(consent));
});
return null;
}This project is licensed under MPL-2.0. Read the full License here