# Persistence and enforcement
Connect UI decisions to your storage and optional services.
Source: https://docs.peax.co/consent/concepts/integration
## Commit decisions at one boundary [#commit-decisions-at-one-boundary]
Treat Accept, Reject and Apply as requests to commit. Construct the correct category map, persist the accepted decision, update service permissions, then close the banner.
Use the same category IDs in the UI, persisted records and service configuration. Retain required categories according to your application policy; do not assume every custom category is optional.
## Restore deliberately [#restore-deliberately]
Your application chooses storage, expiry, schema version and when a visitor must choose again. Validate stored values before using them. If storage is unavailable or invalid, keep optional services inactive and offer a fresh choice.
Restore choices before initializing the quickstart wrapper. A mounted wrapper's `initialPreferences` is an initial value, not a subscription to later storage changes.
## Control optional services [#control-optional-services]
Prevent optional scripts and requests before permission is granted. When a user revokes a category, prevent future activity and update your own stored decision. Disabling a UI switch cannot undo data already sent to another service.
The banner does not provide a script manager, cookie deletion service or cross-tab synchronization. Implement and test those behaviors at the application boundary when your product needs them.
## Test the actual services [#test-the-actual-services]
Check browser network traffic before a choice, after rejection, after acceptance, and after revocation. UI screenshots and callback logs alone cannot establish that scripts are blocked.
---
# State and events
Understand visibility, preference drafts and committed choices.
Source: https://docs.peax.co/consent/concepts/state
## Three separate states [#three-separate-states]
| State | Owner | Meaning |
| ----------------- | ---------------- | ------------------------------------------------- |
| `isOpen` | Your application | Whether the banner is visible |
| Preferences draft | Banner | Uncommitted edits while preferences are open |
| Saved choices | Your application | The decision used for persistence and enforcement |
Opening preferences initializes a draft from the selected sections. Editing an enabled switch emits `on_change_preference`. That message describes a draft, not a consent decision.
Apply emits all selected section IDs in `preferences`, including disabled sections. Your application commits the values, updates the supplied `checked` overrides and changes `isOpen` to `false`.
## Closing and reopening [#closing-and-reopening]
Close or Escape discards the draft and returns to the collapsed banner when preferences were opened from the banner. For floating-button sessions, handle `on_close_preferences` with `source: 'floating-button'` by setting `isOpen={false}`. Dismissal never grants consent. Setting `isOpen={false}` externally also discards the draft, without emitting a navigation message.
The floating button appears while the banner is closed. Handle `on_open_preferences` with `source: 'floating-button'` by setting `isOpen={true}` to open preferences directly. The [complete wrapper](/consent/quickstart) demonstrates an alternative application-owned Cookie choices button and disables the floating button.
## Updating sections [#updating-sections]
Rerenders retain edits for surviving enabled IDs. New IDs use their resolved defaults, removed IDs disappear, and disabled sections retain their resolved value. Changes to an existing enabled section's initial `checked` value apply on the next opening.
Keep configuration stable while a visitor is editing. See the [event reference](/consent/reference/events) for every message.
---
# Interactive examples
Explore Consent layouts, languages and preferences in a live preview.
Source: https://docs.peax.co/consent/examples
Change the layout, language and host theme using the preview above. Turn **Custom preferences** on or off to show or hide the Customize button. **Reset demo** clears the choices you made in the preview.
After accepting or rejecting, use the floating cookie button to reopen preferences. This preview uses `showFloatingButton={true}` and `hasAcceptRejectEqualWeight={false}` to demonstrate the default button emphasis.
[Open the preview in a full page](/_examples/banner) for more room, or follow the [Quickstart](/consent/quickstart) to add Consent to your application.
Peax Studio
In development
## Make Consent your own. [#make-consent-your-own]
Go beyond the presets. Peax Studio is being built to bring your colors, typography and the finer details together in one visual workspace.
[Explore Peax Studio ↗](https://studio.peax.co)
---
# Next.js App Router
Use the controlled banner inside a client boundary and import its stylesheet once.
Source: https://docs.peax.co/consent/frameworks/nextjs
## Install and create the wrapper [#install-and-create-the-wrapper]
Follow the [Quickstart](/consent/quickstart). The `CookieBanner` file must start with `'use client'`.
Import the theme stylesheet in your root layout:
```tsx
import '@peax/consent-banner-theme/styles.css'
import { ConsentIntegration } from './consent-integration'
import type { ReactNode } from 'react'
export default function RootLayout({ children }: { children: ReactNode }) {
return
{children}
}
```
## Keep callbacks in the client boundary [#keep-callbacks-in-the-client-boundary]
```tsx
'use client'
import { useState } from 'react'
import { CookieBanner } from './cookie-banner'
import type { Preferences } from './cookie-banner'
export function ConsentIntegration() {
const [choices, setChoices] = useState(null)
return (
<>
{choices ? 'Demo choices recorded in memory.' : 'No demo choice recorded.'}
>
)
}
```
Copy this integration alongside the wrapper, adjusting its relative import as needed. This example keeps choices in memory; connect your actual storage and optional-service controls before shipping.
When reading saved consent on the server, pass serializable data into the client wrapper and keep first-render values consistent. Keep functions such as `onCommit` inside the client boundary. With client-only storage, wait until restoration completes before initializing the wrapper.
## Verify your integration [#verify-your-integration]
Check a fresh visit, a returning visit, Accept, Reject, Apply and reopening choices. Confirm that optional scripts remain blocked before consent and respond to revocation. See [integration responsibilities](/consent/concepts/integration).
---
# Waku
Integrate the banner through a client component in a Waku application.
Source: https://docs.peax.co/consent/frameworks/waku
## Add the controlled wrapper [#add-the-controlled-wrapper]
Install the packages from the [Quickstart](/consent/quickstart) and copy its `CookieBanner` into a client component. Keep the `'use client'` directive: state and event callbacks must run in the browser.
Import `@peax/consent-banner-theme/styles.css` once from a rendered layout or client component, such as `consent-integration.tsx`. The React package does not load it automatically. Importing global CSS only from a configuration-based `src/router.tsx` does not attach it to rendered pages; keep the import in a component used by those pages.
## Mount from a client integration [#mount-from-a-client-integration]
A server component cannot pass an ordinary callback to a client component. Put the application-owned `onCommit` handler in a client integration component and render that integration from your page or layout.
```tsx
'use client'
import { useState } from 'react'
import { CookieBanner } from './cookie-banner'
import type { Preferences } from './cookie-banner'
export function ConsentIntegration() {
const [choices, setChoices] = useState(null)
return (
<>
{choices ? 'Demo choices recorded in memory.' : 'No demo choice recorded.'}
>
)
}
```
This minimal example holds choices in memory. Replace that boundary with your actual persistence and enforcement adapter before shipping.
## Static rendering and hydration [#static-rendering-and-hydration]
The package is safe to import on the server. Use the same locale, initial choices and visibility for the server render and first client render. Do not access `window` or storage during module initialization.
For browser-only saved choices, render a stable loading state on the server and first client render; resolve storage after mounting, then mount the wrapper. The initial sample always starts with a new visitor and is intended to verify rendering, not durable storage.
Mount the banner outside transformed or clipped ancestors that create restrictive stacking contexts. The docs demonstration isolates it in an iframe for this reason.
---
# Accessibility
Preserve understandable choices, keyboard operation and readable presentation.
Source: https://docs.peax.co/consent/guides/accessibility
## Choice and language [#choice-and-language]
Use clear purpose descriptions and accurate category labels. Optional categories should start unchecked. Keep Accept and Reject equally available; the quickstart sets `hasAcceptRejectEqualWeight`.
## Keyboard behavior [#keyboard-behavior]
The preferences view is a labelled nonmodal region. Opening moves focus to its heading; Tab follows normal page navigation. Disabled switches are skipped. Escape closes preferences while focus is within the panel, returning to the collapsed trigger.
The panel does not trap focus or block the rest of the page. Include an accessible application-owned control for reopening choices after dismissal.
## Presentation checks [#presentation-checks]
Test your customized integration at narrow widths, 200% zoom and increased text spacing. Keep controls visible and labels readable. Verify reduced motion and forced-colors behavior after overrides.
Custom colors must preserve at least 4.5:1 contrast for normal text and 3:1 for meaningful control boundaries and focus indicators. The [theme reference](/consent/reference/theme) identifies the relevant tokens.
## Before shipping [#before-shipping]
Walk through Accept, Reject, preferences editing, cancel, Apply and reopening with keyboard and assistive technology. Automated tools supplement this review; they do not establish complete accessibility or application compliance.
---
# Localization
Use English and German dictionaries and customize visible copy.
Source: https://docs.peax.co/consent/guides/localization
## Select a locale [#select-a-locale]
```tsx
```
Locale resolution trims whitespace, ignores case and falls back from a regional tag to its base language. `en-GB` resolves to English, `de-AT` to German; unsupported or empty inputs use English.
Selection is explicit. Use the same locale for SSR and hydration. There is no browser language detection or storage access.
## Override only what changes [#override-only-what-changes]
`bannerContent` accepts partial banner and preferences messages. `preferencesContent` changes per-section titles and descriptions. Omitted fields retain translated defaults.
```tsx
```
## Policy links [#policy-links]
Translations may contain a flat `label` placeholder. Your application supplies the destination through `cookiePolicyUrl`:
```tsx
Cookie Policy.' }}
/>
```
The renderer creates a link for a usable destination, otherwise plain text. It does not inject arbitrary HTML. Unknown or malformed markup remains literal text.
## Direct dictionaries [#direct-dictionaries]
Import from `@peax/consent-banner-locales/en` or `/de` when you need one language directly. Install locales as a direct dependency when importing it in application code. Spread dictionaries to customize them; do not mutate shared defaults.
See [locale exports](/consent/reference/locales).
---
# Preferences
Choose categories and customize their copy and initial values.
Source: https://docs.peax.co/consent/guides/preferences
## Select sections [#select-sections]
```tsx
```
`preferencesSections` controls order and visibility. Overrides alone do not add sections. The default list is Essential, Analytics and Marketing; `[]` displays no sections.
| ID | Checked | Disabled |
| ----------- | ------- | -------- |
| `essential` | `true` | `true` |
| `analytics` | `false` | `false` |
| `marketing` | `false` | `false` |
| Custom ID | `false` | `false` |
Each `preferencesContent` entry accepts `title`, `text`, `checked` and `disabled`. Omitted fields retain defaults. Explicit `false` is respected; `text: ''` hides a description. Blank titles fall back to a meaningful built-in title or the custom ID.
IDs are case-sensitive. Empty IDs are omitted and duplicates appear once. Provide a localized title for a custom ID:
```tsx
```
## Retain applied choices [#retain-applied-choices]
Store applied values in your application and pass them back as `checked` overrides, as shown in the [quickstart](/consent/quickstart). Handle Accept and Reject for your chosen category IDs as well as Apply.
## Hide the preferences trigger [#hide-the-preferences-trigger]
`showCustomizePreferences={false}` removes the collapsed banner's preferences trigger. It does not close an already open preferences view. Ensure your application still offers the controls your integration requires.
The floating preferences button is independent of this setting. It is enabled by default while `isOpen` is `false`; disable it with `showFloatingButton={false}`. To use it, handle floating-button open and close requests in `onMsg` by updating `isOpen`. See the [event reference](/consent/reference/events).
## Migrate earlier prototypes [#migrate-earlier-prototypes]
Earlier `content` becomes `bannerContent`. Replace a section array with `preferencesSections` IDs and keyed `preferencesContent` overrides. Replace `on_open_settings` and `on_close_settings` with `on_open_preferences` and `on_close_preferences`. Update props, imported types and exhaustive handlers together; old names are not aliases.
---
# Styling
Customize layout, host theme and public CSS variables.
Source: https://docs.peax.co/consent/guides/styling
## Load the stylesheet [#load-the-stylesheet]
```css
@import '@peax/consent-banner-theme/styles.css';
```
Import once at the application entry. There is no separate React CSS entry.
## Choose a layout [#choose-a-layout]
`variant="compact"` provides a wide banner; `variant="stacked"` provides a card. Both support `position="left"`, `"center"` or `"right"`. Sideward `slideFrom` must match the side; center supports bottom entry.
Use `hasAcceptRejectEqualWeight` for equal visual emphasis on the two decision buttons.
## Host theme [#host-theme]
Mode names describe the host page. The default `light` host mode produces a dark banner, and `dark` produces a light banner. `auto` follows the system preference.
## Override tokens [#override-tokens]
```css
.pxc {
--pxc-font-size: 16px;
--pxc-radius-collapsed: 1rem;
--pxc-spacing-lg: 1.25rem;
}
```
Public variables start with `--pxc-`; variables starting with `--_pxc-` are private. Prefer token overrides to selectors targeting internal markup.
For layered overrides, establish cascade order before importing:
```css
@layer peax-consent, app;
@import '@peax/consent-banner-theme/styles.css';
@layer app {
.pxc { --pxc-icon-size: 1.25rem; }
}
```
Unlayered token overrides take precedence over layered defaults. Some component rules remain unlayered to resist host resets.
## Keep custom colors usable [#keep-custom-colors-usable]
Check text contrast, control boundaries and focus indicators in every state and theme. Arbitrary overrides are not repaired by the stylesheet. See [theme reference](/consent/reference/theme) and [accessibility](/consent/guides/accessibility).
---
# Typography
Supply fonts and tune text size, line height and spacing.
Source: https://docs.peax.co/consent/guides/typography
## Font ownership [#font-ownership]
Your application loads font files. The theme makes no remote font requests.
```css
.pxc {
--pxc-font-family: 'Figtree', system-ui, sans-serif;
--pxc-font-family-headings: 'Poppins', system-ui, sans-serif;
font-family: var(--pxc-font-family);
}
```
The body font inherits by default; the heading font falls back to the body font. The stylesheet applies the heading variable to the banner title, preferences title and section headings. Setting `font-family` on the root also gives paragraph text the same body font as the controls.
## Size and density [#size-and-density]
```css
.pxc {
--pxc-font-size: 16px;
--pxc-line-height: 1.6;
--pxc-spacing-md: 1rem;
--pxc-spacing-lg: 1.25rem;
}
```
The default base size is 14px and line height is 1.5. Line height is unitless. Spacing tokens change spacing; they do not change line height.
Allow labels to wrap and content to grow. Test narrow screens, long translations, browser zoom and user text-spacing overrides. Avoid fixed text heights or truncation in consent controls.
---
# Consent
A customizable React consent banner with typed events and built-in preferences.
Source: https://docs.peax.co/consent
Consent provides the interface for asking visitors about their choices. It includes compact and stacked banners, a preferences panel, English and German copy, and a theme built with CSS custom properties.
## Start here [#start-here]
Follow the [quickstart](/consent/quickstart) for a complete controlled integration, then try the [examples](/consent/examples).
## Package responsibilities [#package-responsibilities]
| Package | Responsibility |
| ------------------------------ | -------------------------------------------------------------------- |
| `@peax/consent-banner-react` | React components, temporary preference drafts and interaction events |
| `@peax/consent-banner-theme` | Stylesheet, semantic tokens and TypeScript theme contracts |
| `@peax/consent-banner-locales` | Dictionaries, locale resolution and safe message parsing |
These docs describe **0.2.0** of all three packages. React 18.2+ within 18.x and React 19 are supported, with matching React DOM. Packages provide ESM and TypeScript declarations.
## Your application owns consent [#your-application-owns-consent]
The banner does not store consent, load or block trackers, or communicate with a Peax backend. Connect decision events to your persistence and service controls. Keep optional services inactive until the relevant consent exists.
`@peax/consent-core` has no published runtime API for this integration. No Peax account is required to render the banner.
Read [state and events](/consent/concepts/state) and [persistence and enforcement](/consent/concepts/integration) before shipping.
---
# Quickstart
Install the stylesheet and wire a complete controlled consent flow.
Source: https://docs.peax.co/consent/quickstart
## Install [#install]
npm
pnpm
yarn
bun
```bash
npm install @peax/consent-banner-react@0.2.0 @peax/consent-banner-theme@0.2.0
```
```bash
pnpm add @peax/consent-banner-react@0.2.0 @peax/consent-banner-theme@0.2.0
```
```bash
yarn add @peax/consent-banner-react@0.2.0 @peax/consent-banner-theme@0.2.0
```
```bash
bun add @peax/consent-banner-react@0.2.0 @peax/consent-banner-theme@0.2.0
```
Import the stylesheet once in your application entry:
```css
@import '@peax/consent-banner-theme/styles.css';
```
Locales are included by the React package. Install `@peax/consent-banner-locales@0.2.0` directly when your application imports its dictionaries or types.
## Render and handle choices [#render-and-handle-choices]
This wrapper accepts saved choices through `initialPreferences`. Pass `null` for a new visitor. Its `onCommit` callback is where your application saves the choice and updates optional services.
```tsx
'use client'
import { Banner } from '@peax/consent-banner-react'
import { useState } from 'react'
export type Preferences = Record
type Props = {
initialPreferences: Preferences | null
onCommit: (preferences: Preferences) => void
}
export function CookieBanner({ initialPreferences, onCommit }: Props) {
const [open, setOpen] = useState(initialPreferences === null)
const [preferences, setPreferences] = useState(initialPreferences ?? {})
function commit(next: Preferences) {
onCommit(next) // The application saves this decision and enforces it.
setPreferences(next)
setOpen(false)
}
return (
<>
[id, { checked }]),
)}
onMsg={msg => {
switch (msg.type) {
case 'on_accept_all':
commit({ essential: true, analytics: true, marketing: true })
break
case 'on_reject_all':
commit({ essential: true, analytics: false, marketing: false })
break
case 'on_apply_preferences':
commit(msg.preferences)
break
case 'on_change_preference':
case 'on_open_preferences':
case 'on_close_preferences':
break // Navigation and uncommitted drafts stay inside the banner.
}
}}
/>
>
)
}
```
## Connect your application [#connect-your-application]
Restore saved preferences before mounting this wrapper, keeping server and client initial props consistent. Keep the wrapper mounted so the Cookie choices button can reopen it. This example disables the built-in floating button because it supplies that entry point; see [preferences](/consent/guides/preferences) to use the floating button instead.
The Accept and Reject maps above match the three default categories. Adapt both maps when you change categories. Only Apply, Accept and Reject commit a decision; navigation and draft changes do not.
The example uses `hasAcceptRejectEqualWeight` so Accept and Reject have the same visual weight. Supply a real `/cookies` policy page in your application.
The wrapper expects a synchronous `onCommit`. If your persistence is asynchronous, build an explicit pending/error state and close only after the application accepts the decision. Do not silently dismiss a failed save.
## Choose your framework [#choose-your-framework]
* [Waku](/consent/frameworks/waku): client component and global CSS placement.
* [Next.js App Router](/consent/frameworks/nextjs): client wrapper and root layout integration.
* [Persistence and enforcement](/consent/concepts/integration): storage lifecycle and revocation responsibilities.
---
# Banner API
Public React props and types from the installed 0.2.0 release.
Source: https://docs.peax.co/consent/reference/banner
## Import [#import]
```tsx
import { Banner } from '@peax/consent-banner-react'
import type { BannerProps, BannerMsg, PreferencesSection, PreferencesSectionId } from '@peax/consent-banner-react'
```
Import the stylesheet separately. Only the package root is a supported JavaScript entry.
## Defaults and behavior [#defaults-and-behavior]
| Prop | Default | Behavior |
| ---------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `isOpen` | `false` | Parent-controlled visibility |
| `variant` | `compact` | Compact or stacked layout |
| `position` | `right` | Left, center or right placement |
| `slideFrom` | `bottom` | Bottom or matching side entry |
| `appearanceDelay` | `0` | Initial appearance delay in milliseconds |
| `showCustomizePreferences` | `true` | Show the collapsed preferences trigger |
| `showFloatingButton` | `true` | Show a floating preferences trigger while the banner is closed |
| `floatingButtonPosition` | Derived from `position` | Left for a left banner; right otherwise. Set `showFloatingButton={true}` explicitly when overriding this position |
| `hasAcceptRejectEqualWeight` | `false` | Use equal secondary styling for decisions |
| `locale` | `en` | Explicit locale with English fallback |
| `theme` | `auto` | Host page mode |
| `preferencesSections` | Three built-in IDs | Visible section order |
`bannerContent` and `preferencesContent` merge supplied fields with resolved defaults. `className` and `style` apply to the `.pxc` root. `cookiePolicyUrl` supplies the policy destination. `onMsg` reports interactions; it does not automatically persist or close the banner.
The floating button requests visibility through navigation messages with `source: 'floating-button'`. Handle opening and closing by updating `isOpen`, as described in the [event reference](/consent/reference/events). Set `showFloatingButton={false}` if your application supplies its own preferences entry point.
## Published type signatures [#published-type-signatures]
The following reference is generated from public exports of the installed package, not the development checkout.
```ts
import { BannerMessages } from '@peax/consent-banner-locales';
import { BuiltInPreferencesSectionId } from '@peax/consent-banner-locales';
import { NamedExoticComponent } from 'react';
export declare const Banner: NamedExoticComponent;
export declare type BannerMsg = PreferencesNavigationMsg | {
type: 'on_accept_all' | 'on_reject_all';
} | {
type: 'on_apply_preferences';
preferences: Record;
} | {
type: 'on_change_preference';
sectionId: PreferencesSectionId;
checked: boolean;
};
export declare type BannerProps = Placement & FloatingButtonProps & {
isOpen?: boolean;
appearanceDelay?: number;
hasAcceptRejectEqualWeight?: boolean;
showCustomizePreferences?: boolean;
variant?: 'compact' | 'stacked';
locale?: string;
cookiePolicyUrl?: string;
bannerContent?: Partial;
preferencesSections?: readonly PreferencesSectionId[];
preferencesContent?: Readonly>>;
className?: string;
style?: React.CSSProperties;
theme?: 'light' | 'dark' | 'auto';
onMsg?: (msg: BannerMsg) => void;
};
declare type FloatingButtonPosition = 'left' | 'right';
declare type FloatingButtonProps = {
showFloatingButton?: false;
floatingButtonPosition?: never;
} | {
showFloatingButton: true;
floatingButtonPosition?: FloatingButtonPosition;
};
declare type Placement = {
position?: 'right';
slideFrom?: 'bottom' | 'right';
} | {
position: 'left';
slideFrom?: 'bottom' | 'left';
} | {
position: 'center';
slideFrom?: 'bottom';
};
declare type PreferencesNavigationMsg = {
type: 'on_open_preferences' | 'on_close_preferences';
source: Source;
};
export declare type PreferencesSection = Readonly<{
title?: string;
text?: string;
checked?: boolean;
disabled?: boolean;
}>;
export declare type PreferencesSectionId = BuiltInPreferencesSectionId | (string & Record);
declare type Source = 'banner' | 'floating-button';
export {};
```
---
# Events
Distinguish committed decisions from preference drafts and navigation.
Source: https://docs.peax.co/consent/reference/events
All messages arrive through `onMsg`. The complete union is generated on the [Banner API page](/consent/reference/banner).
| Type | Additional payload | Application action |
| ---------------------- | ---------------------- | --------------------------------------------------------------------------- |
| `on_accept_all` | None | Construct allowed choices, persist, enforce, close |
| `on_reject_all` | None | Retain required choices, reject optional choices, persist, enforce, close |
| `on_apply_preferences` | `preferences` | Commit the selected category map and close |
| `on_change_preference` | `sectionId`, `checked` | Observe a draft edit; do not commit |
| `on_open_preferences` | `source` | Set `isOpen` to `true` when the source is `floating-button` |
| `on_close_preferences` | `source` | Set `isOpen` to `false` when the source is `floating-button`; do not commit |
Apply includes disabled sections and can emit an unchanged selection. An empty section list produces an empty map. Decision messages leave the view open until the application updates `isOpen`.
Navigation messages identify their source as `banner` or `floating-button`. Navigation from the banner changes its internal view; floating-button navigation requests application-controlled visibility:
```tsx
if (msg.type === 'on_open_preferences' && msg.source === 'floating-button') {
setOpen(true)
}
if (msg.type === 'on_close_preferences' && msg.source === 'floating-button') {
setOpen(false)
}
```
Keep handling Accept, Reject and Apply separately to commit choices. Close and Escape discard drafts without granting consent.
---
# Locale API
Language dictionaries, fallback helpers and message parsing.
Source: https://docs.peax.co/consent/reference/locales
## Public entry points [#public-entry-points]
| Entry | Runtime exports |
| ------------ | ---------------------------------------------------------------------------------------------------------------------- |
| Package root | `getMessages`, `getPreferencesMessages`, `resolveLocale`, `parseMessage`, `en`, `de`, `enPreferences`, `dePreferences` |
| `/en` | `en`, `enPreferences` |
| `/de` | `de`, `dePreferences` |
The root includes both dictionaries. Per-language entries include the selected language. All entries are ESM and safe for server-side use.
`resolveLocale` accepts regional tags, trims and normalizes case, and falls back to English. `getMessages` returns banner copy; `getPreferencesMessages` returns section copy, without checked/disabled behavior.
## Published signatures [#published-signatures]
```ts
export declare type BannerLocale = 'en' | 'de';
export declare type BannerMessages = Readonly<{
title: string;
collapsedText: string;
acceptAllLabel: string;
rejectAllLabel: string;
customizePreferencesLabel: string;
preferencesTitle: string;
preferencesText?: string;
closePreferencesLabel: string;
applyPreferencesLabel: string;
cookiePolicyLabel: string;
}>;
export declare type BuiltInPreferencesSectionId = 'essential' | 'analytics' | 'marketing';
export declare const de: BannerMessages;
export declare const dePreferences: PreferencesMessages;
export declare const en: BannerMessages;
export declare const enPreferences: PreferencesMessages;
export declare function getMessages(locale?: string): BannerMessages;
export declare function getPreferencesMessages(locale?: string): PreferencesMessages;
export declare type MessagePart = Readonly<{
offset: number;
value: string;
} & ({
type: 'text';
} | {
type: 'link';
name: Link;
})>;
export declare function parseMessage(message: string, links: readonly Link[]): MessagePart[];
export declare type PreferencesMessages = Readonly>>;
/** Regional tags use the supported base language; unknown locales fall back to English. */
export declare function resolveLocale(locale?: string): BannerLocale;
export {};
```
## Safe message parsing [#safe-message-parsing]
`parseMessage` takes an explicit placeholder allowlist. It returns text and link parts in source order, including source offsets. Unknown, nested, attributed or malformed markup remains literal text; HTML entities are not decoded. Your renderer supplies elements and URLs.
See [localization](/consent/guides/localization) for React examples.
---
# Preference types
Public section IDs and partial per-section overrides.
Source: https://docs.peax.co/consent/reference/preferences
Import `PreferencesSection` and `PreferencesSectionId` from `@peax/consent-banner-react`.
`PreferencesSectionId` suggests built-in IDs while accepting custom strings. `PreferencesSection` is a partial override with `title`, `text`, `checked` and `disabled`; it does not contain an `id` field.
```ts
import type { PreferencesSection, PreferencesSectionId } from '@peax/consent-banner-react'
const ids: PreferencesSectionId[] = ['essential', 'personalization']
const overrides: Record = {
personalization: { title: 'Personalization', checked: false, disabled: false },
}
```
See [preferences configuration](/consent/guides/preferences) for merging and defaults, and [generated signatures](/consent/reference/banner) for the exact installed types.
---
# Theme API and tokens
Public CSS variables and schema-v1 theme types.
Source: https://docs.peax.co/consent/reference/theme
## Stylesheet entry [#stylesheet-entry]
`@peax/consent-banner-theme/styles.css` contains the shared default theme and component styles. JavaScript exports are types-only; fonts belong to your application.
## Public tokens [#public-tokens]
| Group | Variables with the `--pxc-` prefix |
| -------------------- | -------------------------------------------------------------------- |
| Surfaces | `color-surface`, `color-surface-secondary` |
| Text | `color-text`, `color-text-muted` |
| Boundaries and focus | `color-border`, `color-focus-ring` |
| Actions | `color-action`, `color-action-hover`, `color-action-text` |
| Switches | `color-control-track`, `color-control-thumb` |
| Spacing | `spacing-xs`, `spacing-sm`, `spacing-md`, `spacing-lg`, `spacing-xl` |
| Shape | `radius-sm`, `radius-lg`, `radius-xl`, `radius-collapsed` |
| Typography | `font-family`, `font-family-headings`, `font-size`, `line-height` |
| Presentation | `icon-size`, `control-size`, `z-index`, `ease-in-out` |
Set overrides on `.pxc`. Properties starting with `--_pxc-` are implementation details.
Text/surface, muted text/surface, text/secondary surface and action text/action pairs need 4.5:1 contrast. Borders, focus rings, switch tracks and thumbs need 3:1 against adjacent surfaces. Validate hover states as well.
## Published type signatures [#published-type-signatures]
```ts
export declare type ResolvedTheme = {
schemaVersion: ThemeSchemaVersion;
variables: ResolvedThemeVariables;
};
export declare type ResolvedThemeVariables = {
'--pxc-color-surface': string;
'--pxc-color-surface-secondary': string;
'--pxc-color-text': string;
'--pxc-color-text-muted': string;
'--pxc-color-border': string;
'--pxc-color-focus-ring': string;
'--pxc-color-action': string;
'--pxc-color-action-hover': string;
'--pxc-color-action-text': string;
'--pxc-color-control-track': string;
'--pxc-color-control-thumb': string;
'--pxc-spacing-xs': string;
'--pxc-spacing-sm': string;
'--pxc-spacing-md': string;
'--pxc-spacing-lg': string;
'--pxc-spacing-xl': string;
'--pxc-radius-sm': string;
'--pxc-radius-lg': string;
'--pxc-radius-xl': string;
'--pxc-radius-collapsed': string;
'--pxc-font-size': string;
'--pxc-font-family': string;
};
/** Optional presentation overrides; derived tokens are not part of the Studio payload. */
export declare type ThemeOverrides = Partial & {
'--pxc-z-index'?: string;
'--pxc-line-height'?: string;
'--pxc-font-family-headings'?: string;
'--pxc-icon-size'?: string;
'--pxc-control-size'?: string;
'--pxc-ease-in-out'?: string;
};
export declare type ThemeSchemaVersion = 1;
export {};
```
`ResolvedTheme` is a schema-v1 payload. `ThemeOverrides` permits partial customization, including optional presentation tokens. Theme generation and contrast repair are Studio responsibilities; the public stylesheet does not repair arbitrary overrides.
---
# Troubleshooting
Resolve common rendering, styling and state integration problems.
Source: https://docs.peax.co/consent/troubleshooting
## Nothing appears [#nothing-appears]
`isOpen` defaults to `false`. Pass `isOpen={true}` and check `appearanceDelay`. Confirm the banner is not inside a clipped or transformed ancestor.
## The banner is unstyled [#the-banner-is-unstyled]
Import `@peax/consent-banner-theme/styles.css` once in the application entry. The React JavaScript entry intentionally does not import CSS.
## Accept or Apply does not close [#accept-or-apply-does-not-close]
Decision messages are requests. Handle them in `onMsg`, commit the decision and update `isOpen` to `false`.
## Reopened preferences lose their values [#reopened-preferences-lose-their-values]
Save the applied map in the application and pass its values back through `preferencesContent[id].checked`. The banner's internal draft is temporary.
## The floating button does not open or close preferences [#the-floating-button-does-not-open-or-close-preferences]
Handle navigation messages whose `source` is `floating-button`: set `isOpen` to `true` for `on_open_preferences` and `false` for `on_close_preferences`. The floating button requests these changes; the application controls visibility.
## A custom category does not appear [#a-custom-category-does-not-appear]
Include the ID in `preferencesSections`. A `preferencesContent` override alone does not add a section. Update Accept and Reject maps for your chosen IDs.
## Light mode gives a dark banner [#light-mode-gives-a-dark-banner]
Theme names describe the host page. Default banner colors contrast with that host; see [styling](/consent/guides/styling).
## Hydration warnings [#hydration-warnings]
Use matching initial locale, choices and visibility on the server and client. Restore browser-only storage after mounting before initializing the wrapper. Keep hooks and event callbacks behind a client component boundary.
## Overrides do not win [#overrides-do-not-win]
Check cascade order and override public `--pxc-*` tokens on the correct `.pxc` element. Do not depend on private variables or internal markup selectors.
## Newer examples do not typecheck [#newer-examples-do-not-typecheck]
These docs target 0.2.0. Development-branch examples may use features or event fields introduced later. Use the [generated public reference](/consent/reference/banner) as the release-specific contract.
---
# Peax documentation
Build a consent experience that fits your application.
Source: https://docs.peax.co/
Start with a working consent flow, make it your own, and connect it to your application.
## Start building [#start-building]
### Consent [#consent]
A React banner with flexible layouts, themes and localization. Your application stays in control of storage and enforcement.
Current release: **0.2.0**
[Install Consent →](/consent/quickstart)
### Studio [#studio]
Explore the developing theme authoring experience and learn how Studio relates to Consent.
Read the current availability before planning an integration.
[Explore Studio →](/studio)
## Find your next step [#find-your-next-step]
* **Integrate:** follow the [Waku](/consent/frameworks/waku) or [Next.js](/consent/frameworks/nextjs) recipe.
* **Explore:** try the [interactive examples](/consent/examples) to see layouts, themes and preferences in action.
* **Customize:** adjust [styling](/consent/guides/styling) and [localization](/consent/guides/localization) for your product.
* **Look it up:** browse the [API reference](/consent/reference/banner) for props and events.
## Documentation for your tools [#documentation-for-your-tools]
Every page has a Markdown version. Use [the documentation index](/llms.txt) to discover pages or [the complete documentation](/llms-full.txt) for an offline reference.
---
# Studio
Current availability of the Peax theme authoring application.
Source: https://docs.peax.co/studio
[Open Studio](https://studio.peax.co).
## Available today [#available-today]
Studio currently provides its application shell and a light/dark theme control. Its internal theme compiler is under development; the public application does not yet expose a complete editor, live export workflow or account system.
## Use Consent now [#use-consent-now]
You can integrate Consent independently of Studio. Customize public CSS variables with the [styling guide](/consent/guides/styling), and inspect the [theme contract](/consent/reference/theme).
Editor, preview and export guides will be published as those features become available. The current Consent integration requires no Studio account or hosted service.