# 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<string, boolean>

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<Preferences>(initialPreferences ?? {})

	function commit(next: Preferences) {
		onCommit(next) // The application saves this decision and enforces it.
		setPreferences(next)
		setOpen(false)
	}

	return (
		<>
			<button type="button" onClick={() => setOpen(true)}>
				Cookie choices
			</button>
			<Banner
				isOpen={open}
				showFloatingButton={false}
				hasAcceptRejectEqualWeight
				cookiePolicyUrl="/cookies"
				preferencesContent={Object.fromEntries(
					Object.entries(preferences).map(([id, checked]) => [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.
