React / Next.js
Install
npm install @tickboxhq/core @tickboxhq/reactConfig
import { defineConsent, jurisdictions } from '@tickboxhq/core'
export default defineConsent({ jurisdiction: jurisdictions.UK_DUAA, policy: { version: '2026-05-08', url: '/privacy' }, categories: { necessary: { required: true }, analytics: { vendors: ['google-analytics'], default: false }, },})Provider
Wrap your app once. In Next.js App Router, do this in your root layout’s client component.
'use client'import { ConsentProvider } from '@tickboxhq/react'import config from './consent.config'
export function Providers({ children }) { return <ConsentProvider config={config}>{children}</ConsentProvider>}Banner
If you want full control over markup, use the headless component:
import { ConsentBanner } from '@tickboxhq/react'
export function CookieBar() { return ( <ConsentBanner> {({ resolved, grantAll, denyAll, save }) => ( <div className="cookie-bar"> {resolved.map((c) => ( <CategoryRow key={c.id} category={c} /> ))} <button onClick={denyAll}>Reject all</button> <button onClick={grantAll}>Accept all</button> </div> )} </ConsentBanner> )}If you’d rather not design one, drop in the default styled banner — see Default styled banner. It ships with built-in translations for en, de, fr, es, it, nl, pt, pl, uk and a 'auto' mode that reads navigator.language. Multi-language details in the i18n recipe.
Reading consent in components
import { useConsent } from '@tickboxhq/react'
function MarketingPixel() { const { granted } = useConsent('marketing') if (!granted) return null return <script src="..." />}Server-side rendering (Next.js)
useRequestHeaders isn’t available in React/Next.js the way it is in Nuxt, so SSR’d HTML won’t reflect the visitor’s saved consent on first paint. Two options:
- Read the
__tb_consentcookie in your route handler / server component, decode it (it’s URL-encoded JSON), and pass the resulting state to a server component. - Accept a single render flicker on the client — the banner mounts hidden until hydration finishes.
For most sites option 2 is fine. For sites where the flicker is visible, option 1 is documented in Concepts → Script gating.