@c15t/nextjs 2.0.0-rc.9 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,111 @@
1
+ ---
2
+ title: AI Agents
3
+ description: Integrate c15t with AI coding assistants using the docs bundled in each package and c15t agent skills. Give agents version-matched local docs for consent management, banners, script loading, callbacks, and integrations.
4
+ lastModified: 2026-03-24
5
+ ---
6
+ ## Bundled Docs
7
+
8
+ Every supported c15t package now ships docs inside the installed package itself.
9
+
10
+ ### Where to find them
11
+
12
+ * `node_modules/c15t/docs/README.md`
13
+ * `node_modules/@c15t/react/docs/README.md`
14
+ * `node_modules/@c15t/nextjs/docs/README.md`
15
+ * `node_modules/@c15t/backend/docs/README.md`
16
+
17
+ Start with the package `README.md`, then follow its linked pages for the relevant workflow.
18
+
19
+ These docs are version-matched to the exact c15t package version in your project, including generated reference content like prop and type tables.
20
+
21
+ ### Why use them
22
+
23
+ If your app uses multiple c15t packages, use the docs from each relevant installed package instead of relying on stale model knowledge.
24
+
25
+ ### Agent philosophy
26
+
27
+ When an AI tool is helping with c15t behavior, it should read the installed c15t docs first and use model knowledge second. That keeps consent flows, script gating, banner behavior, and integrations aligned with the exact version you have installed.
28
+
29
+ ### Customization ladder for agents
30
+
31
+ When an agent is working on consent UI, it should choose the lowest-power tool that solves the task:
32
+
33
+ 1. Start with the pre-built component and its existing props or provider options
34
+ 2. Use `theme` tokens for semantic visual changes
35
+ 3. Use `theme.slots` for targeted styling of specific parts
36
+ 4. Use CSS variables or className-level overrides only when integrating with external styles
37
+ 5. Use compound components only when the markup order must change
38
+ 6. Use `noStyle` only when c15t structure is still correct but all styling must be replaced
39
+ 7. Use headless hooks only when markup and behavior both need to be rebuilt
40
+
41
+ For common tasks:
42
+
43
+ * Banner footer background -> `theme.colors.surfaceHover`
44
+ * Banner card background -> `theme.colors.surface`
45
+ * Banner card/footer/title tweaks -> banner slots
46
+ * Stock action styling -> `theme.consentActions`
47
+ * Copy changes -> `ConsentManagerProvider.options.i18n`
48
+
49
+ If a token appears not to work, the agent should verify the token-to-component mapping before suggesting CSS overrides, `!important`, `noStyle`, or headless mode.
50
+
51
+ ***
52
+
53
+ ## Agent Skills
54
+
55
+ c15t publishes agent skills that give AI coding assistants deep knowledge of c15t's APIs, components, and configuration. Skills are reusable workflows and tool-specific guidance, not version-matched local docs.
56
+
57
+ ### Installation
58
+
59
+ Via the c15t CLI:
60
+
61
+ |Package manager|Command|
62
+ |:--|:--|
63
+ |npm|`npx @c15t/cli install-skills`|
64
+ |pnpm|`pnpm dlx @c15t/cli install-skills`|
65
+ |yarn|`yarn dlx @c15t/cli install-skills`|
66
+ |bun|`bunx @c15t/cli install-skills`|
67
+
68
+ Or directly:
69
+
70
+ |Package manager|Command|
71
+ |:--|:--|
72
+ |npm|`npx skills add c15t/skills`|
73
+ |pnpm|`pnpm dlx skills add c15t/skills`|
74
+ |yarn|`yarn dlx skills add c15t/skills`|
75
+ |bun|`bunx skills add c15t/skills`|
76
+
77
+ ### What skills provide
78
+
79
+ * **Styling customization** — strict escalation guidance across props, tokens, slots, CSS variables, compound components, `noStyle`, and headless
80
+ * **Internationalization** — translation setup, locale routing integration
81
+ * **Script management** — configuring third-party scripts with consent categories
82
+ * **Component setup** — ConsentBanner, ConsentDialog, provider configuration
83
+
84
+ ### Supported tools
85
+
86
+ * Claude Code
87
+ * Cursor
88
+ * GitHub Copilot (via `.github/skills`)
89
+ * Any agent that supports the skills format
90
+
91
+ ***
92
+
93
+ ## When to use which
94
+
95
+ Use bundled docs when:
96
+
97
+ * Your agent can read files in the local project
98
+ * You want version-matched docs from the installed c15t packages
99
+ * You want a package-local README that tells the agent which detailed docs to read first
100
+ * You want concrete guidance for consent management, cookie banners, consent dialogs, preference centers, script loading, callbacks, and integrations
101
+
102
+ Use agent skills when:
103
+
104
+ * Your tool supports the skills ecosystem
105
+ * You want reusable workflows and tool-specific guidance that can point back to the installed package README files
106
+
107
+ Use both when:
108
+
109
+ * Your tool supports both local file context and skills
110
+ * You want local package docs plus reusable setup and configuration help
111
+ * You want the bundled package docs as the source of truth plus a reusable decision tree for customization
@@ -4,11 +4,11 @@ description: Build policy-aware custom consent components in Next.js using the h
4
4
  ---
5
5
  Building custom consent UI is easier now because c15t exposes multiple layers of policy-aware primitives instead of forcing you to reconstruct banner rules by hand.
6
6
 
7
- The layering is:
7
+ Think of customization as a ladder:
8
8
 
9
9
  * stock component props for the shortest path
10
- * `ConsentBanner.PolicyActions` and `ConsentWidget.PolicyActions` for custom structure with policy-aware actions
11
- * `useHeadlessConsentUI()` for fully manual action rendering and non-standard controls
10
+ * `ConsentBanner.PolicyActions` and `ConsentWidget.PolicyActions` when you want custom structure but still want c15t to resolve policy-aware actions
11
+ * `useHeadlessConsentUI()` when you need fully manual action rendering, custom controls, or non-standard flow
12
12
 
13
13
  > ⚠️ **Warning:**
14
14
  > Headless is the last step in the customization ladder. Use this guide only when pre-built components, tokens, slots, compound components, and noStyle are no longer sufficient.
@@ -26,6 +26,29 @@ The split is intentional: `@c15t/ui` owns pure policy-action resolution, while t
26
26
  > ℹ️ **Info:**
27
27
  > This guide is about building your own components while still respecting resolved policy-pack behavior. For the general headless overview, see Headless Mode.
28
28
 
29
+ ## Choose the Smallest Layer That Solves the Job
30
+
31
+ Start with the smallest API surface that still gives you the behavior you need:
32
+
33
+ * Stay with stock components when you only need theming, spacing, copy, or legal-link changes
34
+ * Use `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions` when you want a custom compound-component layout but still want grouped actions, ordering, and primary emphasis to come from policy
35
+ * Add `renderAction` when the grouping is still correct but you want to remap actions to stock c15t button compounds
36
+ * Reach for `useHeadlessConsentUI()` only when you need custom button elements, need to map `actionGroups` yourself, wire non-button controls, or coordinate the consent UI with a more custom state machine
37
+
38
+ This order matters because every step down the ladder gives you more control, but also makes it easier for your UI to drift away from the resolved policy if you stop using the provided state.
39
+
40
+ ## Before You Build Headless UI
41
+
42
+ Do not use headless mode for problems that are still inside the stock component model:
43
+
44
+ * Use `layout`, `direction`, `primaryButton`, and `legalLinks` before you rebuild banner markup
45
+ * Use `theme.consentActions` before you swap out stock actions
46
+ * Use tokens such as `colors.surface` and `colors.surfaceHover` before raw CSS overrides
47
+ * Use slots such as `consentBannerCard`, `consentBannerFooter`, and `consentDialogCard` before compound components
48
+ * Use `ConsentManagerProvider.options.i18n` before rebuilding UI just to change text
49
+
50
+ A good rule: if the stock banner or dialog structure is still correct, you probably do not need headless mode.
51
+
29
52
  ## What the Headless Tooling Gives You
30
53
 
31
54
  The main win is that your custom UI can stay aligned with policy packs without duplicating policy logic in your components.
@@ -40,9 +63,67 @@ The main win is that your custom UI can stay aligned with policy packs without d
40
63
  * UI profile and scroll-lock hints
41
64
  * whether the banner or dialog should currently be visible
42
65
 
66
+ The hook also gives you the policy-aware action helpers you are expected to call:
67
+
68
+ * `performBannerAction('accept' | 'reject')`
69
+ * `performDialogAction('accept' | 'reject')`
70
+ * `saveCustomPreferences()` for the dialog `customize` action
71
+ * `openDialog()`, `openBanner()`, and `closeUI()` for surface visibility
72
+
43
73
  That means your component mostly focuses on markup and design-system concerns instead of re-implementing policy interpretation.
44
74
 
45
- For most compound-component layouts, start with `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions`. They render stock c15t buttons and translations by default, and `renderAction` is only needed when you want to override the action mapping. Reach for manual `actionGroups` mapping when you need action rendering that no longer fits the stock button compounds.
75
+ For most compound-component layouts, start with `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions`. They render stock c15t buttons and translations by default, and `renderAction` is only needed when you want to override which stock compound renders for each action. Reach for manual `actionGroups` mapping when you need action rendering that no longer fits the stock button compounds.
76
+
77
+ ## Policy-Aware Compound Components First
78
+
79
+ If your goal is "custom layout, same policy behavior", start here before dropping to manual `actionGroups` rendering:
80
+
81
+ ```tsx title="components/consent-manager/banner-shell.tsx"
82
+ 'use client';
83
+
84
+ import { ConsentBanner } from '@c15t/nextjs';
85
+
86
+ export function BannerShell() {
87
+ return (
88
+ <ConsentBanner.Root>
89
+ <ConsentBanner.Card>
90
+ <ConsentBanner.Header>
91
+ <ConsentBanner.Title />
92
+ <ConsentBanner.Description />
93
+ </ConsentBanner.Header>
94
+ <ConsentBanner.PolicyActions />
95
+ </ConsentBanner.Card>
96
+ </ConsentBanner.Root>
97
+ );
98
+ }
99
+ ```
100
+
101
+ Use `renderAction` only when you want to remap actions to stock button compounds while keeping the same policy-driven grouping and ordering:
102
+
103
+ ```tsx title="components/consent-manager/banner-actions.tsx"
104
+ 'use client';
105
+
106
+ import { ConsentBanner } from '@c15t/nextjs';
107
+
108
+ export function BannerActionsWithCustomMapping() {
109
+ return (
110
+ <ConsentBanner.PolicyActions
111
+ renderAction={(action, props) => {
112
+ const { key, ...buttonProps } = props;
113
+
114
+ switch (action) {
115
+ case 'accept':
116
+ return <ConsentBanner.AcceptButton key={key} {...buttonProps} />;
117
+ case 'reject':
118
+ return <ConsentBanner.RejectButton key={key} {...buttonProps} />;
119
+ case 'customize':
120
+ return <ConsentBanner.CustomizeButton key={key} {...buttonProps} />;
121
+ }
122
+ }}
123
+ />
124
+ );
125
+ }
126
+ ```
46
127
 
47
128
  > ℹ️ **Info:**
48
129
  > For custom layouts built from c15t compound components, prefer ConsentBanner.PolicyActions and ConsentWidget.PolicyActions. The examples below intentionally use manual actionGroups mapping to show the fully headless escape hatch.
@@ -89,9 +170,20 @@ export function ConsentManager({ children }: { children: ReactNode }) {
89
170
  import { useHeadlessConsentUI, useTranslations } from '@c15t/nextjs/headless';
90
171
 
91
172
  export function CustomConsentBanner() {
92
- const { banner, openDialog, performAction } = useHeadlessConsentUI();
173
+ const { banner, openDialog, performBannerAction } = useHeadlessConsentUI();
93
174
  const translations = useTranslations();
94
175
 
176
+ function getActionLabel(action: (typeof banner.allowedActions)[number]) {
177
+ switch (action) {
178
+ case 'accept':
179
+ return translations.common.acceptAll;
180
+ case 'reject':
181
+ return translations.common.rejectAll;
182
+ case 'customize':
183
+ return translations.common.customize;
184
+ }
185
+ }
186
+
95
187
  if (!banner.isVisible) return null;
96
188
 
97
189
  return (
@@ -114,14 +206,10 @@ export function CustomConsentBanner() {
114
206
  openDialog();
115
207
  return;
116
208
  }
117
- void performAction(action, { surface: 'banner' });
209
+ void performBannerAction(action);
118
210
  }}
119
211
  >
120
- {action === 'accept'
121
- ? translations.common.acceptAll
122
- : action === 'reject'
123
- ? translations.common.rejectAll
124
- : translations.common.customize}
212
+ {getActionLabel(action)}
125
213
  </button>
126
214
  ))}
127
215
  </div>
@@ -154,6 +242,17 @@ export function CustomConsentDialog() {
154
242
  } = useConsentManager();
155
243
  const translations = useTranslations();
156
244
 
245
+ function getActionLabel(action: (typeof dialog.allowedActions)[number]) {
246
+ switch (action) {
247
+ case 'accept':
248
+ return translations.common.acceptAll;
249
+ case 'reject':
250
+ return translations.common.rejectAll;
251
+ case 'customize':
252
+ return translations.common.save;
253
+ }
254
+ }
255
+
157
256
  if (!dialog.isVisible) return null;
158
257
 
159
258
  const displayedTypes = consentTypes.filter(
@@ -202,11 +301,7 @@ export function CustomConsentDialog() {
202
301
  void performDialogAction(action);
203
302
  }}
204
303
  >
205
- {action === 'accept'
206
- ? translations.common.acceptAll
207
- : action === 'reject'
208
- ? translations.common.rejectAll
209
- : translations.common.save}
304
+ {getActionLabel(action)}
210
305
  </button>
211
306
  ))}
212
307
  </div>
@@ -217,6 +312,17 @@ export function CustomConsentDialog() {
217
312
  }
218
313
  ```
219
314
 
315
+ ## What Headless Is Not For
316
+
317
+ Headless mode is not the recommended path for:
318
+
319
+ * changing the banner footer background
320
+ * rounding the stock banner card
321
+ * restyling stock banner or dialog buttons
322
+ * changing consent copy
323
+
324
+ Those should stay in the pre-built stack with tokens, slots, `theme.consentActions`, and provider `i18n`.
325
+
220
326
  ## What a Policy-Aware Headless Component Should Respect
221
327
 
222
328
  When you build custom banner or dialog components, make sure they use:
@@ -241,36 +241,7 @@ renderAction={(action, props) => {
241
241
  />
242
242
  ```
243
243
 
244
- Use `useTranslations()` only when you are replacing the button markup entirely:
245
-
246
- ```tsx
247
- import { ConsentBanner, useTranslations } from '@c15t/react';
248
-
249
- export function CustomBannerActions() {
250
- const { common } = useTranslations();
251
-
252
- return (
253
- <ConsentBanner.PolicyActions
254
- renderAction={(action, props) => (
255
- <button
256
- key={props.key}
257
- type="button"
258
- className={props.isPrimary ? 'btn-primary' : 'btn-secondary'}
259
- style={props.style}
260
- >
261
- {action === 'accept'
262
- ? common.acceptAll
263
- : action === 'reject'
264
- ? common.rejectAll
265
- : common.customize}
266
- </button>
267
- )}
268
- />
269
- );
270
- }
271
- ```
272
-
273
- For maximum control, use `useHeadlessConsentUI()` and render `banner.actionGroups` manually.
244
+ `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`.
274
245
 
275
246
  If you only need styling changes, stay with tokens and slots instead of rebuilding the banner layout.
276
247
 
@@ -75,7 +75,7 @@ Add a floating button that lets users re-open the dialog after dismissing the ba
75
75
 
76
76
  ## Branding
77
77
 
78
- Hide the c15t branding in the dialog footer:
78
+ Hide the c15t branding tag:
79
79
 
80
80
  ```tsx
81
81
  <ConsentDialog hideBranding />
@@ -84,7 +84,7 @@ Hide the c15t branding in the dialog footer:
84
84
  ## Styling First
85
85
 
86
86
  > ℹ️ **Info:**
87
- > 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, consentDialogHeader, and consentDialogFooter. See Styling Overview.
87
+ > 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.
88
88
 
89
89
  ```tsx
90
90
  <ConsentManagerProvider
@@ -97,7 +97,8 @@ Hide the c15t branding in the dialog footer:
97
97
  slots: {
98
98
  consentDialogCard: 'rounded-[32px] shadow-xl',
99
99
  consentDialogHeader: 'gap-3',
100
- consentDialogFooter: 'border-t border-black/10 px-6',
100
+ consentWidgetFooter: 'gap-3 pt-6',
101
+ consentDialogTag: 'shadow-none',
101
102
  },
102
103
  },
103
104
  }}
@@ -36,15 +36,15 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
36
36
  |Property|Type|Description|Default|Required|
37
37
  |:--|:--|:--|:--|:--:|
38
38
  |enabled|boolean \|undefined|Whether c15t should be active.|true|Optional|
39
- |callbacks|[Callbacks \|undefined](https://v2.c15t.com/docs/frameworks/react/callbacks)|Event callbacks for consent actions.|-|Optional|
40
- |scripts|[Script \|undefined](https://v2.c15t.com/docs/frameworks/react/script-loader)|Dynamically load scripts based on consent state.|-|Optional|
39
+ |callbacks|[Callbacks \|undefined](https://c15t.com/docs/frameworks/react/callbacks)|Event callbacks for consent actions.|-|Optional|
40
+ |scripts|[Script \|undefined](https://c15t.com/docs/frameworks/react/script-loader)|Dynamically load scripts based on consent state.|-|Optional|
41
41
  |legalLinks|Object \|undefined|Configuration for the legal links.|-|Optional|
42
42
  |storageConfig|StorageConfig \|undefined|Storage configuration for consent persistence.|-|Optional|
43
43
  |user|User \|undefined|The user's information. Usually your own internal ID for the user from your auth provider.|-|Optional|
44
44
  |overrides|Overrides \|undefined|Forcefully set values like country, region, language for the consent manager. These values will override the values detected from the browser.|-|Optional|
45
- |networkBlocker|[NetworkBlockerConfig \|undefined](https://v2.c15t.com/docs/frameworks/react/network-blocker)|Configuration for the network request blocker.|-|Optional|
46
- |iab|[IABConfig \|undefined](https://v2.c15t.com/docs/frameworks/react/iab/overview)|IAB TCF 2.3 configuration.|-|Optional|
47
- |ssrData|[Object \|undefined](https://v2.c15t.com/docs/frameworks/react/server-side)|SSR-prefetched data for hydration.|-|Optional|
45
+ |networkBlocker|[NetworkBlockerConfig \|undefined](https://c15t.com/docs/frameworks/react/network-blocker)|Configuration for the network request blocker.|-|Optional|
46
+ |iab|[IABConfig \|undefined](https://c15t.com/docs/frameworks/react/iab/overview)|IAB TCF 2.3 configuration.|-|Optional|
47
+ |ssrData|[Object \|undefined](https://c15t.com/docs/frameworks/react/server-side)|SSR-prefetched data for hydration.|-|Optional|
48
48
 
49
49
  #### `callbacks` Callbacks
50
50
 
@@ -146,9 +146,9 @@ IAB TCF 2.3 configuration.
146
146
 
147
147
  |Property|Type|Description|Default|Required|
148
148
  |:--|:--|:--|:--|:--:|
149
- |enabled|boolean|Enable IAB TCF 2.3 mode. Note: Only works in 'hosted' client mode (legacy alias: 'c15t') because it requires a backend. Options: Fetch GVL from gvl.consent.io; Initialize \_\_tcfapi CMP API; Generate TC Strings for IAB compliance|-|✅ Required|
149
+ |enabled|boolean|Enable IAB TCF 2.3 mode. Note: Only works in 'hosted' client mode (legacy alias: 'c15t') because it requires a backend. Options: Fetch GVL from gvl.inth.app; Initialize \_\_tcfapi CMP API; Generate TC Strings for IAB compliance|-|✅ Required|
150
150
  |\_module|IABModule \|undefined|IAB runtime module injected by \`@c15t/iab\`.|-|Optional|
151
- |cmpId|number \|undefined|CMP ID registered with IAB Europe. When using consent.io as the backend, this is automatically provided via the \`/init\` endpoint — no client-side configuration needed. Only set this if you self-host and have your own CMP registration. A valid (non-zero) CMP ID is required for IAB TCF compliance.|-|Optional|
151
+ |cmpId|number \|undefined|CMP ID registered with IAB Europe. When using inth.com as the backend, this is automatically provided via the \`/init\` endpoint — no client-side configuration needed. Only set this if you self-host and have your own CMP registration. A valid (non-zero) CMP ID is required for IAB TCF compliance.|-|Optional|
152
152
  |cmpVersion|string \|number \|undefined|CMP version. When omitted, defaults to package version from \`\~/cmp-defaults\` (which uses \~/version).|-|Optional|
153
153
  |vendors|number\[] \|undefined|IAB-registered vendor IDs to include (optional). Used to scope the vendor list when fetching GVL or when hosted fallback paths are used (e.g. if GVL fetch fails).|-|Optional|
154
154
  |customVendors|NonIABVendor \|undefined|Custom vendors not registered with IAB. These are displayed separately in the consent UI with a note that they have different privacy practices than IAB vendors.|-|Optional|
@@ -171,8 +171,8 @@ SSR-prefetched data for hydration.
171
171
  |Property|Type|Description|Default|Required|
172
172
  |:--|:--|:--|:--|:--:|
173
173
  |i18n|I18nConfig \|undefined|Preferred i18n configuration in c15t v2.|-|Optional|
174
- |translations|[TranslationConfig \|undefined](https://v2.c15t.com/docs/frameworks/react/internationalization)|Translation configuration to seed the store with.|-|Optional|
175
- |consentCategories|[AllConsentNames \|undefined](https://v2.c15t.com/docs/frameworks/react/concepts/consent-categories)|Consent categories to show in the consent banner.|-|Optional|
174
+ |translations|[TranslationConfig \|undefined](https://c15t.com/docs/frameworks/react/internationalization)|Translation configuration to seed the store with.|-|Optional|
175
+ |consentCategories|[AllConsentNames \|undefined](https://c15t.com/docs/frameworks/react/concepts/consent-categories)|Consent categories to show in the consent banner.|-|Optional|
176
176
 
177
177
  #### `i18n` I18nConfig
178
178
 
@@ -198,12 +198,12 @@ Translation configuration to seed the store with.
198
198
 
199
199
  |Property|Type|Description|Default|Required|
200
200
  |:--|:--|:--|:--|:--:|
201
- |theme|[Theme \|undefined](https://v2.c15t.com/docs/frameworks/react/styling/tokens)|Visual theme to apply.|-|Optional|
201
+ |theme|[Theme \|undefined](https://c15t.com/docs/frameworks/react/styling/tokens)|Visual theme to apply.|-|Optional|
202
202
  |disableAnimation|boolean \|undefined|Whether to disable animations.|false|Optional|
203
203
  |scrollLock|boolean \|undefined|Whether to lock scroll when dialogs are open.|false|Optional|
204
204
  |trapFocus|boolean \|undefined|Whether to trap focus within dialogs.|true|Optional|
205
- |colorScheme|["light" \|"dark" \|"system" \|undefined](https://v2.c15t.com/docs/frameworks/react/styling/color-scheme)|Color scheme preference. With this option, you can force the theme to be light, dark or system. Otherwise, the theme will be detected if you have '.dark' classname in your document.|-|Optional|
206
- |noStyle|[boolean \|undefined](https://v2.c15t.com/docs/frameworks/react/headless)|Whether to disable default styles.|false|Optional|
205
+ |colorScheme|["light" \|"dark" \|"system" \|undefined](https://c15t.com/docs/frameworks/react/styling/color-scheme)|Color scheme preference. With this option, you can force the theme to be light, dark or system. Otherwise, the theme will be detected if you have '.dark' classname in your document.|-|Optional|
206
+ |noStyle|[boolean \|undefined](https://c15t.com/docs/frameworks/react/headless)|Whether to disable default styles.|false|Optional|
207
207
 
208
208
  #### `theme` Theme
209
209
 
@@ -392,7 +392,7 @@ Read the full guide at [Policy Packs](/docs/frameworks/react/policy-packs) and t
392
392
  |Property|Type|Description|Default|Required|
393
393
  |:--|:--|:--|:--|:--:|
394
394
  |children|ReactNode|React children to render within the provider.|-|✅ Required|
395
- |options|[ConsentManagerOptions](https://v2.c15t.com/docs/frameworks/react/components/consent-manager-provider)|Configuration options for the consent manager. This includes core, React, store, and translation settings.|-|✅ Required|
395
+ |options|[ConsentManagerOptions](https://c15t.com/docs/frameworks/react/components/consent-manager-provider)|Configuration options for the consent manager. This includes core, React, store, and translation settings.|-|✅ Required|
396
396
 
397
397
  #### `options` ConsentManagerOptions
398
398
 
@@ -117,34 +117,7 @@ renderAction={(action, props) => {
117
117
  />
118
118
  ```
119
119
 
120
- Use `useTranslations()` only when you are replacing the button markup entirely:
121
-
122
- ```tsx
123
- import { ConsentWidget, useTranslations } from '@c15t/react';
124
-
125
- export function CustomWidgetActions() {
126
- const { common } = useTranslations();
127
-
128
- return (
129
- <ConsentWidget.PolicyActions
130
- renderAction={(action, props) => (
131
- <button
132
- key={props.key}
133
- type="button"
134
- className={props.isPrimary ? 'btn-primary' : 'btn-secondary'}
135
- style={props.style}
136
- >
137
- {action === 'accept'
138
- ? common.acceptAll
139
- : action === 'reject'
140
- ? common.rejectAll
141
- : common.save}
142
- </button>
143
- )}
144
- />
145
- );
146
- }
147
- ```
120
+ `renderAction` is still meant for stock button compounds. If you want completely custom button elements and handlers, use `useHeadlessConsentUI()` and render `dialog.actionGroups` manually instead of `ConsentWidget.PolicyActions`.
148
121
 
149
122
  For a fixed footer layout, render `ConsentWidget.Footer` and `ConsentWidget.FooterSubGroup` manually instead of using `ConsentWidget.PolicyActions`.
150
123
 
@@ -54,6 +54,39 @@ export function ConsentManager({ children }: { children: ReactNode }) {
54
54
  |**Events**|Timeline of consent events and state changes|
55
55
  |**Actions**|Buttons to trigger consent actions (accept all, reject all, reset)|
56
56
 
57
+ ## TanStack Devtools
58
+
59
+ `@c15t/dev-tools/tanstack` exposes a panel component and plugin factory that match TanStack Devtools' plugin API, so c15t can sit beside Query and Router without a custom mount adapter:
60
+
61
+ ```tsx
62
+ import * as React from 'react';
63
+ import { useRouter } from '@tanstack/react-router';
64
+ import { TanStackDevtools } from '@tanstack/react-devtools';
65
+ import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
66
+ import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
67
+ import { c15tDevtools } from '@c15t/dev-tools/tanstack';
68
+
69
+ export function AppDevtools() {
70
+ const router = useRouter();
71
+
72
+ return (
73
+ <TanStackDevtools
74
+ plugins={[
75
+ {
76
+ name: 'TanStack Query',
77
+ render: <ReactQueryDevtoolsPanel />,
78
+ },
79
+ {
80
+ name: 'TanStack Router',
81
+ render: <TanStackRouterDevtoolsPanel router={router} />,
82
+ },
83
+ c15tDevtools(),
84
+ ]}
85
+ />
86
+ );
87
+ }
88
+ ```
89
+
57
90
  ## Props
58
91
 
59
92
  ### C15TDevToolsProps
@@ -15,7 +15,7 @@ c15t supports three client modes that determine how consent data is stored and s
15
15
 
16
16
  ## Hosted Mode (Recommended)
17
17
 
18
- The default mode. Connects to a c15t backend for full consent lifecycle management. We recommend using [consent.io](https://consent.io) for a fully managed experience, but you can [self-host](/docs/self-host) as well.
18
+ The default mode. Connects to a c15t backend for full consent lifecycle management. We recommend using [inth.com](https://inth.com) for a fully managed experience, but you can [self-host](/docs/self-host) as well.
19
19
 
20
20
  > ℹ️ **Info:**
21
21
  > mode: 'hosted' is the preferred value. The legacy alias mode: 'c15t' is still supported for backward compatibility.
@@ -8,7 +8,7 @@ A policy pack is an ordered array of policies. Each policy targets a region or c
8
8
 
9
9
  There are three ways to configure policy packs:
10
10
 
11
- 1. **consent.io (recommended)** — use [consent.io](https://consent.io) as your hosted backend. Configure packs visually in the dashboard or via API — no code changes required. Works with any frontend, including static sites.
11
+ 1. **inth.com (recommended)** — use [inth.com](https://inth.com) as your hosted backend. Configure packs visually in the dashboard or via API — no code changes required. Works with any frontend, including static sites.
12
12
  2. **Self-hosted backend** — define packs in code via `policyPacks` and resolve them from real request geo data. Full control over policy logic and storage.
13
13
  3. **Offline fallback** — pass the same policy shapes to the frontend via `offlinePolicy.policyPacks`. Use this mainly for local development, demos, deterministic testing, or resilience when the backend is temporarily unreachable. If you omit `offlinePolicy.policyPacks`, c15t falls back to a synthetic worldwide opt-in banner instead of no-banner mode.
14
14
 
@@ -87,7 +87,7 @@ Information about when and how consent was given
87
87
  |:--|:--|:--|:--|:--:|
88
88
  |time|number|The epoch timestamp of when the consent was recorded|-|✅ Required|
89
89
  |subjectId|string \|undefined|The client-generated subject ID in sub\_xxx format|-|Optional|
90
- |id|string \|undefined|Configuration for the legal links @remarks Legal links can display across different parts of the consent manager such as the consent banner & dialog.|-|Optional|
90
+ |id|string \|undefined|Effective GPC signal used for the request.|-|Optional|
91
91
  |externalId|string \|undefined|The external user ID linked to this subject|-|Optional|
92
92
  |materialPolicyFingerprint|string \|undefined|Material fingerprint of the active policy when this consent was accepted.|-|Optional|
93
93
  |identityProvider|string \|undefined|The identity provider that provided the external ID|-|Optional|
@@ -189,6 +189,7 @@ IAB TCF 2.3 state and actions (null when not configured or not in IAB mode).
189
189
  |setOverrides|Object \|undefined \|null|Sets the overrides for the consent manager. Automatically attempts to fetch the consent manager again with the new overrides.|-|✅ Required|
190
190
  |setLanguage|Object \|undefined \|null|Set the language override for the consent manager. This will override the language detected from the browser and re-fetch the consent banner information.|-|✅ Required|
191
191
  |identifyUser|Object|Identifies the user by setting the external ID.|-|✅ Required|
192
+ |unstable\_acceptPolicyConsent|Object \|undefined|Writes a policy-based consent such as terms and conditions.|-|✅ Required|
192
193
  |setSelectedConsent|(name: AllConsentNames, value: boolean) => void|Updates the selected consent state for a specific consent type.|-|✅ Required|
193
194
  |saveConsents|Object \|undefined|Saves the user's consent preferences.|-|✅ Required|
194
195
  |setConsent|(name: AllConsentNames, value: boolean) => void|Updates the consent state for a specific consent type & automatically save the consent.|-|✅ Required|
@@ -246,6 +247,21 @@ Identifies the user by setting the external ID.
246
247
  |id|string|Usually your own internal ID for the user from your auth provider|-|✅ Required|
247
248
  |identityProvider|string \|undefined|The identity provider of the user. Usually the name of the identity provider e.g. 'clerk', 'auth0', 'custom', etc.|-|Optional|
248
249
 
250
+ #### `unstable_acceptPolicyConsent`
251
+
252
+ Writes a policy-based consent such as terms and conditions.
253
+
254
+ |Property|Type|Description|Default|Required|
255
+ |:--|:--|:--|:--|:--:|
256
+ |type|Object|-|-|✅ Required|
257
+ |domain|string \|undefined|-|-|Optional|
258
+ |givenAt|number \|undefined|-|-|Optional|
259
+ |metadata|Record\<string, unknown> \|undefined|-|-|Optional|
260
+ |preferences|Record\<string, boolean> \|undefined|-|-|Optional|
261
+ |uiSource|string \|undefined|-|-|Optional|
262
+ |externalId|string \|undefined|-|-|Optional|
263
+ |identityProvider|string \|undefined|-|-|Optional|
264
+
249
265
  #### `subscribeToConsentChanges`
250
266
 
251
267
  Subscribes to change-only consent saves.
@@ -344,7 +360,7 @@ function LoginForm() {
344
360
  ```
345
361
 
346
362
  > ℹ️ **Info:**
347
- > identifyUser sends the user data to the c15t backend. It only works in 'c15t' mode — in 'offline' mode the call is a no-op.
363
+ > identifyUser sends the user data to the c15t backend. It works in hosted mode, including the legacy alias mode: 'c15t'. In mode: 'offline', the call is a no-op.
348
364
 
349
365
  ## Key Types
350
366
 
@@ -2,8 +2,8 @@
2
2
  title: IABConsentBanner
3
3
  description: An IAB TCF 2.3 compliant consent banner that displays partner count, purpose summaries, and legitimate interest notices.
4
4
  ---
5
- > **Error:**
6
- > c15t is not yet IAB certified. The IAB TCF components are under active development and should not be used in production. APIs and behavior may change before certification is achieved.
5
+ > ℹ️ **Info:**
6
+ > c15t's IAB TCF support can be used in production. Use Inth for a hosted, IAB TCF-certified CMP setup with a managed CMP ID, or register your own CMP with IAB Europe and configure your own CMP ID.
7
7
 
8
8
  `IABConsentBanner` is a pre-built consent banner that follows the [IAB Transparency & Consent Framework (TCF) 2.3](https://iabeurope.eu/tcf-2-0/) specification. It renders when the consent model is set to `'iab'` and includes required disclosures like partner count, purpose summaries, and legitimate interest notices.
9
9
 
@@ -19,6 +19,7 @@ Use this component instead of `ConsentBanner` when you need IAB TCF compliance f
19
19
 
20
20
  ```tsx
21
21
  import { type ReactNode } from 'react';
22
+ import { iab } from '@c15t/iab';
22
23
  import { ConsentManagerProvider } from '@c15t/nextjs';
23
24
  import { IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
24
25
 
@@ -28,11 +29,12 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
28
29
  options={{
29
30
  mode: 'hosted',
30
31
  backendURL: '/api/c15t',
31
- iab: {
32
- enabled: true,
33
- cmpId: 123,
32
+ iab: iab({
34
33
  vendors: [1, 2, 10, 25],
35
- },
34
+ // cmpId is automatically provided by the backend when using Inth.
35
+ // Only set this if you have your own CMP registration.
36
+ // cmpId: 123,
37
+ }),
36
38
  }}
37
39
  >
38
40
  <IABConsentBanner />