React SDK
- Written for
- + Written for
- Deprecated
- + Deprecated
- Applies to
- + Applies to
React SDK
@cookiemunch/react is a thin React/Next.js wrapper around the Cookie Munch browser embed (consent.js). It does not bundle the consent engine itself — at runtime it injects the same <script data-cbid="..."> tag you'd otherwise hand-write, waits for the embed to assign window.CookieMunch, and exposes that object's state and actions through a context, a hook, and a gating component. Because the engine stays out of your bundle, this package has no dependency on @cookiemunch/core — it only depends on react (>=18) as a peer dependency, and ships pre-built (no separate build step in your app).
If you already have consent.js on the page some other way (Google Tag Manager, a <script> tag in your HTML template, a previous SSR render), ConsentProvider detects the existing window.CookieMunch and reuses it instead of injecting a second copy.
Install
npm install @cookiemunch/react
# or
pnpm add @cookiemunch/react
# or
yarn add @cookiemunch/reactQuick start
Wrap your app (or just the part of the tree that needs consent state) in <ConsentProvider>, giving it your site id (cbid):
// app/layout.tsx (Next.js App Router) or your root component
import { ConsentProvider } from '@cookiemunch/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<ConsentProvider cbid="your-site-id">{children}</ConsentProvider>
</body>
</html>
);
}That's it — the banner itself is rendered by the embed script, not by React. The provider's job is purely to load consent.js and mirror its state into React so the rest of your component tree can read it and react to it.
ConsentProviderProps
Prop | Type | Required | Description |
|---|---|---|---|
|
| yes | Your Cookie Munch site id. Injected as |
|
| no | URL of the embed script. Defaults to |
|
| no | Overrides the API origin the embed beacons consent decisions to. Injected as |
|
| yes | Your app tree. |
ConsentProvider is SSR-safe: it never touches window or document during render, only inside an effect, so it's safe to mount in a Next.js Server Component boundary (the provider itself must be a Client Component, but its children don't have to be). Until the embed reports in, descendants see a not-ready, all-denied snapshot (necessary: true, everything else false) rather than throwing or blocking render.
If cbid, src, or apiBase change on a re-render, the provider re-binds to the (possibly new) embed instance.
Reading consent with useConsent()
Call useConsent() from any descendant of ConsentProvider. It throws if called outside one, so you'll find out immediately if the provider is missing higher up the tree.
import { useConsent } from '@cookiemunch/react';
function CookieSettingsLink() {
const { show, ready } = useConsent();
if (!ready) return null;
return <button onClick={show}>Cookie settings</button>;
}useConsent() returns a ConsentContextValue:
Field | Type | Description |
|---|---|---|
|
|
|
|
| The current per-category snapshot (see below). |
|
|
|
|
|
|
|
| Grants every category. |
|
| Denies every non-necessary category. |
|
| Records a specific per-category decision, bypassing the banner UI — e.g. for a custom preferences form you build in React. |
|
| Revokes all non-necessary consent and re-blocks previously activated scripts. |
|
| Opens the phase-1 banner. |
|
| Re-prompts on the site's configured surface (banner, or straight to preferences if configured that way). |
consent is a PublicConsent object:
interface PublicConsent {
necessary: boolean;
preferences: boolean;
statistics: boolean;
marketing: boolean;
personalizedAds: boolean; // granular IAB TCF signal, falls back to `marketing`
basicAds: boolean;
adProfiling: boolean;
adMeasurement: boolean;
stamp: string; // the consent-receipt id
[key: string]: boolean | string; // custom categories, if your config defines any
}Before the embed has reported in (ready === false), consent reads as { necessary: true, preferences: false, statistics: false, marketing: false, ... , stamp: '' } — treat that as "no decision yet," not as an explicit decline.
Because submitCustomConsent bypasses the embed's own UI, you can also build a fully custom preferences screen in React — a form that toggles checkboxes and calls submitCustomConsent(preferences, statistics, marketing) on submit — and it still writes through the same engine, ledger, and receipt stamp as the stock banner.
Gating components with <ConsentGate>
<ConsentGate> renders its children only when a given category is granted, and a fallback (default null) otherwise. It's a thin wrapper over useConsent() — use it when you want to keep a component tree declarative rather than branching on consent[category] yourself:
import { ConsentGate } from '@cookiemunch/react';
function Page() {
return (
<ConsentGate category="marketing" fallback={<p>Enable marketing cookies to see this widget.</p>}>
<RetargetingPixel />
</ConsentGate>
);
}ConsentGateProps
Prop | Type | Required | Description |
|---|---|---|---|
|
| yes | The category to gate on. |
|
| yes | Rendered when |
|
| no | Rendered when it isn't. Defaults to |
ConsentGate re-renders automatically whenever consent changes, because it reads from the same context ConsentProvider updates. There's no polling or manual refresh needed.
Reacting to consent changes
useConsent()'s returned values already update on every accept/decline, so the most common pattern is simply reading consent inside a component and letting React re-render it. To run an imperative side effect (e.g. lazily initializing a third-party SDK) when a category newly becomes granted, combine it with useEffect:
import { useConsent } from '@cookiemunch/react';
import { useEffect } from 'react';
function AnalyticsBootstrap() {
const { consent, ready } = useConsent();
useEffect(() => {
if (ready && consent.statistics) {
initAnalytics();
}
}, [ready, consent.statistics]);
return null;
}This mirrors what window.CookieMunch.onConsentChange() does at the embed level (see the JavaScript API docs) — ConsentProvider already subscribes to that event and flows updates through React state, so you never call onConsentChange yourself from React code.
Notes
There is no
<ConsentBanner>export in@cookiemunch/react— banner UI is rendered by the embed script itself (configured from your Cookie Munch dashboard), not by React.ConsentProvideronly bridges its state into your component tree. If you want a fully custom banner, hide the stock one in your config and drivesubmitCustomConsent/acceptAll/declineAllfrom your own UI as shown above.Everything exported (
ConsentProvider,useConsent,ConsentGate, and theCategory/PublicConsent/CookieMunchGlobaltypes) is available from the package root:import { ... } from '@cookiemunch/react'.Types for
window.CookieMunchare declared locally in this package (not imported from@cookiemunch/core), so installing@cookiemunch/reactalone is enough to get full typing in a project that never installs the core engine directly.