---
title: ConsentBanner
description: A pre-built consent banner that appears when user consent is needed. Supports policy-aware layout, theming, and advanced composition when markup must change.
---
`ConsentBanner` is a ready-to-use consent banner that appears automatically in **opt-in jurisdictions** (like GDPR) where explicit consent is required before tracking. In opt-out jurisdictions (like CCPA), the banner won't appear — users get an opt-out mechanism instead. It includes reject, accept, and customize buttons with configurable layout.

## Basic Usage

```tsx
import { ConsentManagerProvider, ConsentBanner } from '@c15t/nextjs';

export function ConsentManager() {
  return (
    <ConsentManagerProvider options={{ mode: 'hosted', backendURL: '/api/c15t' }}>
      <ConsentBanner />
    </ConsentManagerProvider>
  );
}
```

## Button Layout

The `layout` prop controls button arrangement. Each item is either a button ID or an array of button IDs (which groups them together):

```tsx
{/* Default: reject and accept grouped, customize separate */}
<ConsentBanner layout={[['reject', 'accept'], 'customize']} />

{/* All buttons in one group */}
<ConsentBanner layout={[['reject', 'customize', 'accept']]} />

{/* Accept first, then reject and customize grouped */}
<ConsentBanner layout={['accept', ['reject', 'customize']]} />
```

To stack groups vertically, pair the same grouped layout with `direction="column"`:

```tsx
<ConsentBanner
  layout={['customize', ['reject', 'accept']]}
  direction="column"
/>
```

## Policy-Driven UI Profile

When using backend runtime policies, `policy.ui.uiProfile` can control banner action presentation:

* `compact` — default desktop sizing
* `balanced` — auto-fills compact and grouped layouts with moderate emphasis
* `strict` — always fills action controls for explicit, high-clarity layouts

## Theme-Level Button Styling

Use the provider `theme` prop to control how stock consent actions look:

```tsx
<ConsentManagerProvider
  options={{
    theme: {
      consentActions: {
        default: { mode: 'stroke' },
        accept: { variant: 'primary', mode: 'stroke' },
        customize: { variant: 'neutral', mode: 'ghost' },
      },
    },
  }}
>
  <ConsentBanner />
</ConsentManagerProvider>
```

Policy packs control grouping, ordering, and direction. The theme controls button appearance.

## Styling First

> ℹ️ **Info:**
> For pure theming, stay inside the pre-built banner. Start with layout props, theme.consentActions, design tokens, and theme.slots before reaching for compound components. See Styling Overview.

The stock banner maps common visual changes to the theme system:

* Card background -> `theme.colors.surface`
* Footer background -> `theme.colors.surfaceHover`
* Card, footer, and title tweaks -> `theme.slots.consentBannerCard`, `consentBannerFooter`, and `consentBannerTitle`

```tsx
<ConsentManagerProvider
  options={{
    theme: {
      colors: {
        surface: '#fffdf8',
        surfaceHover: '#f6f3ee',
      },
      slots: {
        consentBannerCard: 'rounded-[28px] shadow-xl',
        consentBannerFooter: 'border-t border-black/10 px-6',
        consentBannerTitle: 'tracking-tight',
      },
    },
  }}
>
  <ConsentBanner />
</ConsentManagerProvider>
```

### Primary Button

Highlight specific button(s) as the primary action:

```tsx
{/* Single primary */}
<ConsentBanner primaryButton="accept" />

{/* Multiple primaries */}
<ConsentBanner primaryButton={['accept', 'customize']} />
```

## Legal Links

Control which legal links appear in the banner description:

```tsx
{/* Show all configured links (default) */}
<ConsentBanner legalLinks={undefined} />

{/* Show no links */}
<ConsentBanner legalLinks={null} />

{/* Show specific links */}
<ConsentBanner legalLinks={['privacyPolicy', 'cookiePolicy']} />
```

> ℹ️ **Info:**
> Legal link URLs are configured in the ConsentManagerProvider options via the legalLinks prop, not on the banner itself.

## Customizing Copy

Prefer provider `i18n` when you want to rename the stock banner content:

```tsx
<ConsentManagerProvider
  options={{
    i18n: {
      locale: 'en',
      messages: {
        en: {
          cookieBanner: {
            title: 'We value your privacy',
            description: 'We use cookies to improve the site and measure performance.',
          },
          common: {
            acceptAll: 'Accept all',
            rejectAll: 'Reject all',
            customize: 'Manage preferences',
          },
        },
      },
    },
  }}
>
  <ConsentBanner />
</ConsentManagerProvider>
```

Direct text props such as `title`, `description`, and `acceptButtonText` are still supported for one-off overrides, but `i18n` is the preferred path for copy changes.

## Advanced: Compound Components

Use compound components only when the stock banner structure is no longer enough and you need to rearrange existing c15t primitives while keeping policy-driven action grouping and emphasis:

```tsx
<ConsentBanner.Root>
  <ConsentBanner.Overlay />
  <ConsentBanner.Card>
    <ConsentBanner.Header>
      <ConsentBanner.Title />
      <ConsentBanner.Description />
    </ConsentBanner.Header>
    <ConsentBanner.PolicyActions />
  </ConsentBanner.Card>
</ConsentBanner.Root>
```

* `ConsentBanner.Root` — Outermost container, provides theme context
* `ConsentBanner.Card` — Main content card with optional focus trapping
* `ConsentBanner.Header` — Contains title and description
* `ConsentBanner.Title` — Heading, defaults to translation `consentBanner.title`
* `ConsentBanner.Description` — Description text, supports `legalLinks` prop
* `ConsentBanner.PolicyActions` — Renders policy-aware grouped actions inside the banner footer
* `ConsentBanner.Footer` — Action buttons container
* `ConsentBanner.FooterSubGroup` — Groups related buttons together
* `ConsentBanner.RejectButton` — Rejects all consent
* `ConsentBanner.CustomizeButton` — Opens the consent dialog
* `ConsentBanner.AcceptButton` — Accepts all consent
* `ConsentBanner.Overlay` — Optional backdrop overlay

For a fixed layout that intentionally ignores policy grouping, render the footer manually:

```tsx
<ConsentBanner.Root>
  <ConsentBanner.Card>
    <ConsentBanner.Header>
      <ConsentBanner.Title />
      <ConsentBanner.Description />
    </ConsentBanner.Header>
    <ConsentBanner.Footer>
      <ConsentBanner.FooterSubGroup>
        <ConsentBanner.RejectButton />
        <ConsentBanner.AcceptButton />
      </ConsentBanner.FooterSubGroup>
      <ConsentBanner.CustomizeButton />
    </ConsentBanner.Footer>
  </ConsentBanner.Card>
</ConsentBanner.Root>
```

## Using `renderAction` with c15t Defaults

`ConsentBanner.PolicyActions` renders stock c15t buttons and translations by default.

```tsx
<ConsentBanner.PolicyActions />
```

`renderAction` is optional. When you want custom mapping but still want the built-in c15t button behavior and copy, return the stock button compounds:

```tsx
<ConsentBanner.PolicyActions
renderAction={(action, props) => {
  const { key, ...buttonProps } = props

  switch (action) {
    case 'accept':
        return <ConsentBanner.AcceptButton key={key} {...buttonProps} />
    case 'reject':
        return <ConsentBanner.RejectButton key={key} {...buttonProps} />
    case 'customize':
        return <ConsentBanner.CustomizeButton key={key} {...buttonProps} />
  }
}}
/>
```

`renderAction` is still meant for stock button compounds. If you want completely custom button elements and click handling, use `useHeadlessConsentUI()` and render `banner.actionGroups` manually instead of `ConsentBanner.PolicyActions`.

If you only need styling changes, stay with tokens and slots instead of rebuilding the banner layout.

## Props

| Property            | Type                                                       | Description                                                                                                                                                                                                           | Default     | Required |
| :------------------ | :--------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------: |
| noStyle             | boolean \| undefined                                       | When true, removes all default styling from the component                                                                                                                                                             | false       | Optional |
| title               | ReactNode                                                  | Content to display as the banner's title                                                                                                                                                                              | undefined   | Optional |
| description         | ReactNode                                                  | Content to display as the banner's description                                                                                                                                                                        | undefined   | Optional |
| rejectButtonText    | ReactNode                                                  | Content to display on the reject button                                                                                                                                                                               | undefined   | Optional |
| customizeButtonText | ReactNode                                                  | Content to display on the customize button                                                                                                                                                                            | undefined   | Optional |
| acceptButtonText    | ReactNode                                                  | Content to display on the accept button                                                                                                                                                                               | undefined   | Optional |
| scrollLock          | boolean \| undefined                                       | When true, the consent banner will lock the scroll of the page                                                                                                                                                        | false       | Optional |
| trapFocus           | boolean \| undefined                                       | When true, the consent banner will trap focus                                                                                                                                                                         | true        | Optional |
| disableAnimation    | boolean \| undefined                                       | When true, disables the entrance/exit animations                                                                                                                                                                      | false       | Optional |
| legalLinks          | any                                                        | Controls which legal links to display.&#xA;&#xA;- \`undefined\` (default): Shows all available legal links&#xA;- \`null\`: Explicitly hides all legal links&#xA;- Array of keys: Shows only the specified legal links | -           | Optional |
| hideBranding        | boolean \| undefined                                       | When true, hides the branding tag on the banner.                                                                                                                                                                      | false       | Optional |
| layout              | ConsentBannerLayout \| undefined                           | Defines the layout of buttons in the footer.&#xA;Allows reordering and grouping of buttons.                                                                                                                           | -           | Optional |
| direction           | any                                                        | Defines how footer button groups flow.                                                                                                                                                                                | -           | Optional |
| primaryButton       | ConsentBannerButton \| ConsentBannerButton\[] \| undefined | Specifies which button(s) should be highlighted as the primary action.                                                                                                                                                | -           | Optional |
| models              | any\[] \| undefined                                        | Which consent models this banner responds to.                                                                                                                                                                         | \['opt-in'] | Optional |
| uiSource            | string \| undefined                                        | Override the UI source identifier sent with consent API calls.                                                                                                                                                        | 'banner'    | Optional |
