---
title: ConsentDialog
description: A modal dialog where users can toggle individual consent categories.
---
`ConsentDialog` is a modal that shows toggles for each consent category. In **opt-in jurisdictions**, it typically opens when users click "Customize" on `ConsentBanner`. It can also be controlled programmatically for use on settings pages regardless of jurisdiction.

## Basic Usage

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

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

## Controlled State

By default, the dialog follows `activeUI === 'dialog'` from the consent store. Use the `open` prop for manual control:

```tsx
import { useState } from 'react';

function SettingsPage() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Privacy Settings</button>
      <ConsentDialog open={open} />
    </>
  );
}
```

Or use the hook to open it programmatically:

```tsx
import { useConsentManager } from '@c15t/nextjs';

function PrivacyLink() {
  const { setActiveUI } = useConsentManager();

  return (
    <button onClick={() => setActiveUI('dialog')}>
      Manage cookies
    </button>
  );
}
```

## Floating Trigger

Add a floating button that lets users re-open the dialog after dismissing the banner:

```tsx
{/* Default trigger */}
<ConsentDialog showTrigger />

{/* Custom trigger */}
<ConsentDialog
  showTrigger={{
    icon: 'settings',
    defaultPosition: 'bottom-left',
    showWhen: 'after-consent',
    size: 'sm',
  }}
/>
```

## Branding

Hide the c15t branding tag:

```tsx
<ConsentDialog hideBranding />
```

## Styling First

> ℹ️ **Info:**
> If you are only changing visuals, stay with the stock dialog and use the theme system first. Start with tokens and slots such as consentDialogCard, consentWidgetFooter, and consentDialogTag. See Styling Overview.

```tsx
<ConsentManagerProvider
  options={{
    theme: {
      colors: {
        surface: '#fffdf8',
        surfaceHover: '#f6f3ee',
      },
      slots: {
        consentDialogCard: 'rounded-[32px] shadow-xl',
        consentDialogHeader: 'gap-3',
        consentWidgetFooter: 'gap-3 pt-6',
        consentDialogTag: 'shadow-none',
      },
    },
  }}
>
  <ConsentDialog />
</ConsentManagerProvider>
```

Dialog copy should be changed through `ConsentManagerProvider.options.i18n`, not by rebuilding the dialog structure.

## Advanced: Compound Components

Use compound components only when you need custom dialog markup while still keeping c15t primitives and policy-aware footer actions:

```tsx
<ConsentDialog.Root>
  <ConsentDialog.Overlay />
  <ConsentDialog.Card>
    <ConsentDialog.Header>
      <ConsentDialog.HeaderTitle />
      <ConsentDialog.HeaderDescription />
    </ConsentDialog.Header>
    <ConsentDialog.Content>
      <ConsentWidget.Root>
        <ConsentWidget.Accordion type="single">
          <ConsentWidget.AccordionItems />
        </ConsentWidget.Accordion>
        <ConsentWidget.PolicyActions />
      </ConsentWidget.Root>
    </ConsentDialog.Content>
    <ConsentDialog.Footer />
  </ConsentDialog.Card>
</ConsentDialog.Root>
```

* `ConsentDialog.Root` — Portal container with focus trap, scroll lock, and animation
* `ConsentDialog.Card` — Main dialog card
* `ConsentDialog.Header` — Contains title and description
* `ConsentDialog.HeaderTitle` — Dialog title
* `ConsentDialog.HeaderDescription` — Description with optional `legalLinks`
* `ConsentDialog.Content` — Main content area (typically contains `ConsentWidget`)
* `ConsentDialog.Footer` — Footer with optional branding (`hideBranding` prop)
* `ConsentDialog.Overlay` — Backdrop overlay
* `ConsentWidget.PolicyActions` — Renders policy-aware grouped dialog actions

For a quick pre-composed layout, use the shorthand card:

```tsx
<ConsentDialog.Root>
  <ConsentDialog.ConsentCustomizationCard />
</ConsentDialog.Root>
```

`ConsentWidget.PolicyActions` uses stock c15t widget buttons and translations by default. Pass `renderAction` only when you need to customize the action mapping, and return stock widget button compounds if you want to preserve built-in behavior and copy.

For fully manual control over dialog action rendering, use `useHeadlessConsentUI()` and map `dialog.actionGroups` yourself.

If the stock dialog structure still works, prefer tokens, slots, and provider configuration instead.

## Props

| Property         | Type                 | Description                                                                                                                                                                                                                                                                                                  | Default                | Required |
| :--------------- | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------- | :------: |
| theme            | undefined            | Theme configuration override (deprecated)                                                                                                                                                                                                                                                                    | -                      | Optional |
| disableAnimation | boolean \| undefined | Disables all animations when true                                                                                                                                                                                                                                                                            | -                      | Optional |
| noStyle          | boolean \| undefined | Removes default styles when true                                                                                                                                                                                                                                                                             | -                      | Optional |
| scrollLock       | boolean \| undefined | Locks the scroll when true                                                                                                                                                                                                                                                                                   | -                      | Optional |
| trapFocus        | boolean \| undefined | Traps keyboard focus within a dialog when true                                                                                                                                                                                                                                                               | -                      | Optional |
| open             | boolean \| undefined | Control the open state. If omitted the dialog follows&#xA;\`useConsentManager().activeUI === 'dialog'\`.                                                                                                                                                                                                     | -                      | 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 | Controls whether to hide the branding in the dialog footer.                                                                                                                                                                                                                                                  | -                      | Optional |
| showTrigger      | any                  | Show a floating trigger button to resurface the consent dialog.&#xA;Useful for allowing users to re-open the dialog after dismissing.&#xA;&#xA;- \`true\` - Show trigger with default settings&#xA;- \`false\` - Hide trigger (default)&#xA;- \`ConsentDialogTriggerProps\` - Show trigger with custom props | false                  | Optional |
| models           | any\[] \| undefined  | Which consent models this dialog responds to.                                                                                                                                                                                                                                                                | \['opt-in', 'opt-out'] | Optional |
| uiSource         | string \| undefined  | Override the UI source identifier sent with consent API calls.                                                                                                                                                                                                                                               | 'dialog'               | Optional |
