Cookie Consent
ProA GDPR-ready consent surface in two coordinated pieces over one shared state: a non-blocking banner and a preferences dialog with a toggle per cookie category. The parts are the real DS components.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
CookieConsent,
CookieBanner,
CookieBannerIcon,
CookieBannerContent,
CookieBannerTitle,
CookieBannerDescription,
CookieBannerDetails,
CookieDetailList,
CookieBannerActions,
CookieBannerDetailsTrigger,
CookieCustomizeButton,
CookieRejectAllButton,
CookieAcceptAllButton,
CookiePreferences,
CookiePreferencesTrigger,
CookiePreferencesContent,
CookiePreferencesHeader,
CookiePreferencesTitle,
CookiePreferencesDescription,
CookieCategoryList,
CookiePreferencesFooter,
CookieSavePreferencesButton,
} from "@/components/ui/cookie-consent"
<CookieConsent>
<CookieBanner>
<CookieBannerIcon />
<CookieBannerContent>
<CookieBannerTitle />
<CookieBannerDescription />
</CookieBannerContent>
<CookieBannerDetails>
<CookieDetailList />
</CookieBannerDetails>
<CookieBannerActions>
<CookieBannerDetailsTrigger />
<CookieCustomizeButton />
<CookieRejectAllButton />
<CookieAcceptAllButton />
</CookieBannerActions>
</CookieBanner>
<CookiePreferences>
<CookiePreferencesTrigger />
<CookiePreferencesContent>
<CookiePreferencesHeader>
<CookiePreferencesTitle />
<CookiePreferencesDescription />
</CookiePreferencesHeader>
<CookieCategoryList />
<CookiePreferencesFooter>
<CookieSavePreferencesButton />
</CookiePreferencesFooter>
</CookiePreferencesContent>
</CookiePreferences>
</CookieConsent>Details on hover#
CookieBannerDetails keeps the banner clean: it stays folded away and unrolls above the copy on hover or keyboard focus, so the buttons never move under the pointer. CookieDetailList fills it from your categories, listing the cookies of each one with its purpose, provider and duration. Touch screens have no hover, so CookieBannerDetailsTrigger shows there, and only there. Escape folds it again.
Essential cookies only#
A site that stores only what it needs to work (a sign-in, a saved theme) needs no consent, so there is nothing to accept or reject, only something to know. layout="launcher" rests the banner as its icon alone in a small circle. On hover, focus or a tap, the circle grows into the card and the icon slides to its seat beside the title, with the reason and the full list above. This is the notice koalaui.com runs.
Remember the choice#
Pass storageKey and the root keeps the answer in localStorage: the banner shows only until the visitor answers, the preferences come back on the next visit, and every open tab follows. The record is dated and versioned. Bump version when your categories change and everyone is asked again; after maxAgeDays (six months by default) the choice expires and the banner returns. Nothing optional is on until the visitor says so, so gate each script on the preferences map.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Preferences dialog#
The configurator is a DS Dialog bound to the same shared state. CookieCategoryList maps your categories into rows, each with a leading icon, a description, and a Switch. Required categories render locked-on with an Always on badge. Open it from the banner's CookieCustomizeButton, or from a standalone CookiePreferencesTrigger anywhere on the page.
Position#
position anchors the banner: bottom is a full-bleed bar that rows its content out on wide screens, while bottom-left and bottom-right are compact floating cards. Each slides in from its own edge. layout arranges the parts inside: stack (the default) is a column, inline one slim row, and launcher (corners only) the icon alone until the banner is expanded.
API reference#
CookieConsent
The root and single source of truth: owns the category booleans, the dialog's open state and the three actions; renders no DOM. Every part that renders an element accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| categories* | CookieCategoryDef[] | - | The categories to manage, in display order: { id, label, description?, required?, icon?, cookies? }. |
| value | Record<string, boolean> | - | Controlled preferences map, { [id]: boolean }. Required categories are always clamped on. |
| defaultValue | Record<string, boolean> | - | Initial preferences when uncontrolled; a stored answer wins over it. Anything unset is off. |
| onValueChange | (value: Record<string, boolean>) => void | - | Called whenever a toggle or action changes the map. |
| onAcceptAll | (value: Record<string, boolean>) => void | - | Called when the visitor accepts every category. |
| onRejectAll | (value: Record<string, boolean>) => void | - | Called when the visitor rejects all but the required categories. |
| onSave | (value: Record<string, boolean>) => void | - | Called when the visitor saves the current selection. |
| open | boolean | - | Controlled open state of the preferences dialog. |
| defaultOpen | boolean | false | Initial dialog open state when uncontrolled. |
| onOpenChange | (open: boolean) => void | - | Called when the dialog opens or closes. |
| defaultConsented | boolean | false | Start with the banner hidden, for a visitor whose answer you stored yourself. |
| storageKey | string | - | Remember the answer in localStorage under this key, dated and versioned, and keep every tab in step. Omit to persist it yourself. |
| version | number | string | 1 | Your cookie policy's version; an answer stored under another one no longer counts, so the banner asks again. |
| maxAgeDays | number | 180 | Days a stored answer stays valid before the banner asks again. Needs storageKey. |
| density | "comfortable" | "compact" | "compact" | Banner padding (20 or 16px) and category rows; the dialog follows it too. Falls back to the nearest DensityProvider. |
useCookieConsent
Reads the root's state from a custom surface: prefs, categories, setPreference(id, value), acceptAll(), rejectAll(), save(), dialogOpen, setDialogOpen(open) and bannerVisible. Call it inside CookieConsent.
CookieBanner
The non-blocking consent card, a fixed <div role="region"> that animates in, and out once a choice is made; forwards all native <div> props. Declares --surface, so nested controls blend with it.
| Prop | Type | Default | Description |
|---|---|---|---|
| position | "bottom" | "bottom-left" | "bottom-right" | "bottom-right" | bottom is a full-bleed bar that rows out from lg; the corners are floating cards. |
| layout | "stack" | "inline" | "launcher" | "stack" | A column, one slim row, or (corners only, needs CookieBannerIcon) the icon alone in a circle that grows into the inline card on hover, focus or tap. |
| Data attribute | Description |
|---|---|
| data-state | "open" while visible, "closed" during the exit animation. |
| data-expanded | Present while the details layer (or a launcher) is open. |
| CSS variable | Description |
|---|---|
| --surface | Set to var(--popover), read by nested controls. |
CookieBannerIcon
An optional leading glyph: pass a Koala icon, sized to 20px. Forwards all native <div> props.
CookieBannerContent
The title and description column. Forwards all native <div> props.
CookieBannerTitle
The banner's heading. Forwards all native <p> props.
CookieBannerDescription
Muted supporting copy, usually with a link to your policy. Forwards all native <p> props.
CookieBannerDetails
A layer folded away at rest that unrolls upward from the top of the banner on hover, keyboard focus or CookieBannerDetailsTrigger; Escape folds it. Forwards all native <div> props.
| Data attribute | Description |
|---|---|
| data-state | "open" or "closed". |
CookieBannerDetailsTrigger
Opens and closes the details layer on touch screens, hidden where a pointer can hover. Without children it is an icon-only caret that turns as the layer opens; forwards every Button prop.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | ButtonProps["variant"] | "ghost" | Any Button variant. |
CookieBannerActions
The action cluster; stacked corner cards stretch its buttons into an even row. Forwards all native <div> props.
CookieDetailList
Every category with the cookies it sets (name, duration, purpose, provider), read from the root's categories; a category without cookies shows its description. Forwards all native <div> props.
| CSS variable | Description |
|---|---|
| --cookie-row | Each line's place in the stagger inside CookieBannerDetails, set per line. |
CookiePreferences
The preferences dialog root, a Koala Dialog bound to the root's open state; renders no DOM. Forwards all Dialog props.
CookiePreferencesTrigger
Opens the dialog from your own element, Koala's DialogTrigger. Renders a <button> and forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Merge onto your own element, such as a footer link. |
CookiePreferencesContent
The dialog panel, Koala's DialogContent with the root's density; forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "xl" | "md" | Dialog width; md is 480px. |
CookiePreferencesHeader
Koala's DialogHeader, re-exported so the dialog reads from one import.
CookiePreferencesTitle
Koala's DialogTitle, which names the dialog for assistive tech.
CookiePreferencesDescription
Koala's DialogDescription.
CookiePreferencesFooter
Koala's DialogFooter, the row for the save and reject buttons.
CookieCategoryList
Maps the root's categories into CookieCategory rows inside one bordered container with dividers, a role="group". Takes className only.
CookieCategory
One row: icon, a <label> for the Switch, description and the Switch. A required category shows an Always on badge and a disabled, checked Switch.
| Prop | Type | Default | Description |
|---|---|---|---|
| category* | CookieCategoryDef | - | The category to render; its id keys into the preferences map. |
CookieAcceptAllButton
A Koala Button with accept-all bound; forwards every Button prop, and your onClick runs after the action.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | ButtonProps["variant"] | "primary" | Any Button variant. |
| children | React.ReactNode | "Accept all" | The label. |
CookieRejectAllButton
A Koala Button with reject-all bound; forwards every Button prop, and your onClick runs after the action.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | ButtonProps["variant"] | "secondary" | Any Button variant. |
| children | React.ReactNode | "Reject all" | The label. |
CookieCustomizeButton
A Koala Button with opening the preferences dialog bound; forwards every Button prop, and your onClick runs after the action.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | ButtonProps["variant"] | "ghost" | Any Button variant. |
| children | React.ReactNode | "Customize" | The label. |
CookieSavePreferencesButton
A Koala Button with saving the current selection bound; forwards every Button prop, and your onClick runs after the action.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | ButtonProps["variant"] | "primary" | Any Button variant. |
| children | React.ReactNode | "Save preferences" | The label. |