Skip to main content

React Components

ConsentDialogTrigger

Add the trigger inside ConsentProvider

ConsentDialogTrigger is a draggable floating button that opens the preference center. Place it once inside ConsentProvider, next to the banner and dialog from the quickstart, so visitors can revisit their choices without a footer link.

src/consent.tsx
import { ConsentDialogTrigger } from 'c15t/react';

// Inside the ConsentProvider from your quickstart, next to the dialog:
<ConsentDialogTrigger showWhen="after-prompt" />;

showWhen="after-prompt" keeps the button out of the way while a choice or notice is still owed and shows it once the visitor has answered, so the banner and the trigger never compete for the same corner. The default, always, shows it whenever the preference center is closed.

The button carries data-c15t-rights with the rights the active policy guarantees, so a stylesheet can label it differently under an opt-out rule.

Props

PropTypeDefaultDescription
icon'branding' | 'fingerprint' | 'settings' | ReactNode'branding'Icon rendered inside the button.
defaultPosition'bottom-left' | 'bottom-right' | 'top-left' | 'top-right''bottom-right'Corner the button starts in.
persistPositionbooleantrueRemember the corner the visitor dragged it to.
showWhen'always' | 'after-prompt' | 'never''always'When the button is visible. after-prompt waits until no choice or notice is owed.
size'sm' | 'md' | 'lg''md'Button size.
ariaLabelstring'Open privacy settings'Accessible name.
noStylebooleanfalseRemove the default styling.

Configurable toolbar

Use ConsentDialogTriggerToolbar when you want app-owned controls beside the privacy trigger. It always renders exactly one built-in action that opens the preference center, so actions only holds controls your app owns.

import { ConsentDialogTriggerToolbar } from 'c15t/react';

<ConsentDialogTriggerToolbar
	ariaLabel="Site controls"
	actions={[
		{
			id: 'theme',
			label: isDark ? 'Switch to light theme' : 'Switch to dark theme',
			icon: isDark ? <SunIcon /> : <MoonIcon />,
			pressed: isDark,
			onSelect: toggleColorScheme,
		},
		{
			id: 'support',
			label: 'Open support chat',
			icon: <ChatIcon />,
			onSelect: openSupportChat,
		},
	]}
	preferences={{ icon: 'fingerprint', label: 'Manage privacy settings' }}
/>;

Each custom action needs a stable id, an accessible label, an icon, and an onSelect callback. Use pressed for toggle actions and disabled for unavailable ones. Your app owns the state behind each action. The preferences action moves to the edge nearest the toolbar's snapped corner while custom actions keep their configured order.

Toolbars are horizontal by default. Set orientation="vertical" to stack the actions and switch arrow-key navigation to up and down:

<ConsentDialogTriggerToolbar orientation="vertical" actions={toolbarActions} />;

When the controls show

Your toolbar actions do not depend on the consent policy, so a theme toggle stays available while a banner is up. The built-in preferences action, and the single ConsentDialogTrigger, follow the policy:

SituationBuilt-in action and ConsentDialogTrigger
A choice or notice is still owed, with showWhen="after-prompt"Hidden until the visitor answers
showWhen="never" on the toolbarLeft out of the toolbar
No policy rule has resolved: resolution failed, no rule matched and you set no default, or init is still withheldHidden; appears without a remount once a rule resolves
The rule has model: 'none'Hidden, unless the rule adds rights: ['preferences']
Any other caseShown

On the toolbar, showWhen applies only to the built-in action. On the single ConsentDialogTrigger, it applies to the whole control. The toolbar renders nothing only when it has no visible item.

What the built-in action says

  • Under a rule with the opt-out right, such as a US opt-out notice, its accessible name is the translated "Do not sell or share my data" and it carries data-right="opt-out".
  • Under any other rule it reads "Manage preferences" and carries data-right="preferences".
  • Either way it opens the preference center. A preferences.label you pass replaces the default name, and the button carries data-c15t-rights with every right on the rule.

Toolbar props

PropTypeDefaultDescription
actionsConsentDialogTriggerToolbarAction[][]App-owned actions rendered beside the preferences action.
preferencesConsentDialogTriggerToolbarPreferences{}Overrides for the built-in action: icon, label, onSelect, className, style. The label defaults to the opt-out or preferences right on the active rule.
orientation'horizontal' | 'vertical''horizontal'Layout direction.
defaultPositionCornerPosition'bottom-right'Corner the toolbar starts in.
persistPositionbooleantrueRemember the dragged corner.
showWhen'always' | 'after-prompt' | 'never''always'When the built-in preferences action is visible. App-owned actions always render. after-prompt waits until no choice or notice is owed.
size'sm' | 'md' | 'lg''md'Size of each action.
ariaLabelstring'Privacy controls'Accessible name for the toolbar group.
noStylebooleanfalseRemove the default styling.

While <ConsentDevTools> is mounted, the toolbar adds a DevTools button at the end farthest from its corner and opens the DevTools panel beside itself. The ready-made ConsentDialogTrigger switches to this toolbar layout for as long as DevTools is mounted.

Toolbar styling

The toolbar follows the standard precedence: bundled styles, then provider slots, then direct className and style on the toolbar or on one action. Use noStyle for a fully custom implementation.

<ConsentProvider
	options={{
		mode,
		components: {
			trigger: {
				toolbar: { className: 'my-toolbar' },
				toolbarItem: { className: 'my-toolbar-item' },
				toolbarIcon: { className: 'my-toolbar-icon' },
			},
		},
	}}
>
	<ConsentDialogTriggerToolbar
		className="fixed-toolbar"
		actions={[
			{
				id: 'support',
				label: 'Open support chat',
				icon: <ChatIcon />,
				onSelect: openSupportChat,
				className: 'support-action',
			},
		]}
	/>
</ConsentProvider>;

The toolbar slot keys are trigger.toolbar, trigger.toolbarItem, and trigger.toolbarIcon. With noStyle, style the current position and interaction state through data-corner, data-dragging, and data-snapping on the toolbar element.