---
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/react';

export function ConsentManager() {
  return (
    <ConsentManagerProvider options={{ mode: 'hosted', backendURL: 'https://your-instance.c15t.dev' }}>
      <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/react';

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

### ConsentDialogProps

|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 \`useConsentManager().activeUI === 'dialog'\`.|-|Optional|
|legalLinks|(keyof LegalLinksTranslations)\[] \|null \|undefined|Controls which legal links to display. Options: \`undefined\` (default): Shows all available legal links; \`null\`: Explicitly hides all legal links; 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|ConsentDialogTriggerProps \|undefined|Show a floating trigger button to resurface the consent dialog. Useful for allowing users to re-open the dialog after dismissing. Options: \`true\` - Show trigger with default settings; \`false\` - Hide trigger (default); \`ConsentDialogTriggerProps\` - Show trigger with custom props|false|Optional|
|models|Model \|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|
