@c15t/nextjs 2.0.0-rc.6 → 2.0.0-rc.8

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.
Files changed (59) hide show
  1. package/README.md +7 -0
  2. package/dist/iab/styles.css +12 -1
  3. package/dist/iab/styles.tw3.css +14 -0
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/styles.css +10 -1
  7. package/dist/styles.tw3.css +13 -0
  8. package/dist/version.cjs +1 -1
  9. package/dist/version.js +1 -1
  10. package/dist-types/headless.d.ts +1 -1
  11. package/dist-types/index.d.ts +2 -2
  12. package/dist-types/libs/browser-initial-data.d.ts +2 -2
  13. package/dist-types/libs/initial-data.d.ts +1 -1
  14. package/dist-types/types.d.ts +3 -3
  15. package/dist-types/version.d.ts +1 -1
  16. package/docs/building-headless-components.md +43 -22
  17. package/docs/callbacks.md +76 -9
  18. package/docs/components/consent-banner.md +148 -24
  19. package/docs/components/consent-dialog.md +42 -3
  20. package/docs/components/consent-manager-provider.md +3 -1
  21. package/docs/components/consent-widget.md +91 -9
  22. package/docs/concepts/client-modes.md +16 -4
  23. package/docs/concepts/initialization-flow.md +9 -2
  24. package/docs/concepts/policy-packs.md +2 -2
  25. package/docs/headless.md +13 -7
  26. package/docs/hooks/use-consent-manager/overview.md +17 -3
  27. package/docs/hooks/use-ssr-status.md +1 -1
  28. package/docs/hooks/use-translations.md +1 -0
  29. package/docs/iab/consent-banner.md +2 -5
  30. package/docs/iab/consent-dialog.md +3 -6
  31. package/docs/iab/overview.md +11 -5
  32. package/docs/integrations/building-integrations.md +405 -0
  33. package/docs/integrations/databuddy.md +22 -5
  34. package/docs/integrations/google-tag-manager.md +2 -2
  35. package/docs/integrations/google-tag.md +2 -29
  36. package/docs/integrations/linkedin-insights.md +1 -1
  37. package/docs/integrations/meta-pixel.md +1 -1
  38. package/docs/integrations/microsoft-uet.md +1 -1
  39. package/docs/integrations/overview.md +18 -2
  40. package/docs/integrations/posthog.md +39 -17
  41. package/docs/integrations/tiktok-pixel.md +1 -1
  42. package/docs/integrations/x-pixel.md +1 -1
  43. package/docs/optimization.md +68 -9
  44. package/docs/policy-packs.md +7 -7
  45. package/docs/quickstart.md +11 -5
  46. package/docs/script-loader.md +22 -1
  47. package/docs/server-side.md +1 -1
  48. package/docs/styling/classnames.md +17 -9
  49. package/docs/styling/overview.md +166 -29
  50. package/docs/styling/slots.md +37 -7
  51. package/docs/styling/tailwind.md +25 -27
  52. package/iab/styles.css +1 -0
  53. package/package.json +15 -7
  54. package/readme.json +4 -0
  55. package/src/iab/styles.css +12 -0
  56. package/src/iab/styles.tw3.css +14 -0
  57. package/src/styles.css +10 -0
  58. package/src/styles.tw3.css +13 -0
  59. package/styles.css +1 -0
@@ -81,9 +81,36 @@ Hide the c15t branding in the dialog footer:
81
81
  <ConsentDialog hideBranding />
82
82
  ```
83
83
 
84
- ## Compound Components
84
+ ## Styling First
85
85
 
86
- Build fully custom dialog layouts using sub-components:
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.
88
+
89
+ ```tsx
90
+ <ConsentManagerProvider
91
+ options={{
92
+ theme: {
93
+ colors: {
94
+ surface: '#fffdf8',
95
+ surfaceHover: '#f6f3ee',
96
+ },
97
+ slots: {
98
+ consentDialogCard: 'rounded-[32px] shadow-xl',
99
+ consentDialogHeader: 'gap-3',
100
+ consentDialogFooter: 'border-t border-black/10 px-6',
101
+ },
102
+ },
103
+ }}
104
+ >
105
+ <ConsentDialog />
106
+ </ConsentManagerProvider>
107
+ ```
108
+
109
+ Dialog copy should be changed through `ConsentManagerProvider.options.i18n`, not by rebuilding the dialog structure.
110
+
111
+ ## Advanced: Compound Components
112
+
113
+ Use compound components only when you need custom dialog markup while still keeping c15t primitives and policy-aware footer actions:
87
114
 
88
115
  ```tsx
89
116
  <ConsentDialog.Root>
@@ -94,7 +121,12 @@ Build fully custom dialog layouts using sub-components:
94
121
  <ConsentDialog.HeaderDescription />
95
122
  </ConsentDialog.Header>
96
123
  <ConsentDialog.Content>
97
- <ConsentWidget />
124
+ <ConsentWidget.Root>
125
+ <ConsentWidget.Accordion type="single">
126
+ <ConsentWidget.AccordionItems />
127
+ </ConsentWidget.Accordion>
128
+ <ConsentWidget.PolicyActions />
129
+ </ConsentWidget.Root>
98
130
  </ConsentDialog.Content>
99
131
  <ConsentDialog.Footer />
100
132
  </ConsentDialog.Card>
@@ -109,6 +141,7 @@ Build fully custom dialog layouts using sub-components:
109
141
  * `ConsentDialog.Content` — Main content area (typically contains `ConsentWidget`)
110
142
  * `ConsentDialog.Footer` — Footer with optional branding (`hideBranding` prop)
111
143
  * `ConsentDialog.Overlay` — Backdrop overlay
144
+ * `ConsentWidget.PolicyActions` — Renders policy-aware grouped dialog actions
112
145
 
113
146
  For a quick pre-composed layout, use the shorthand card:
114
147
 
@@ -118,6 +151,12 @@ For a quick pre-composed layout, use the shorthand card:
118
151
  </ConsentDialog.Root>
119
152
  ```
120
153
 
154
+ `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.
155
+
156
+ For fully manual control over dialog action rendering, use `useHeadlessConsentUI()` and map `dialog.actionGroups` yourself.
157
+
158
+ If the stock dialog structure still works, prefer tokens, slots, and provider configuration instead.
159
+
121
160
  ## Props
122
161
 
123
162
  ### ConsentDialogProps
@@ -54,6 +54,7 @@ Event callbacks for consent actions.
54
54
  |:--|:--|:--|:--|:--:|
55
55
  |onBannerFetched|Callback\<OnBannerFetchedPayload> \|undefined|Called when the consent banner is fetched.|-|Optional|
56
56
  |onConsentSet|Callback\<OnConsentSetPayload> \|undefined|Called when the consent is set.|-|Optional|
57
+ |onConsentChanged|Callback\<OnConsentChangedPayload> \|undefined|Called only when an explicit consent save changes the previously saved consent state.|-|Optional|
57
58
  |onError|Callback\<OnErrorPayload> \|undefined|Called when an error occurs.|-|Optional|
58
59
  |onBeforeConsentRevocationReload|Callback\<OnConsentSetPayload> \|undefined|Called before the page reloads when consent is revoked.|-|Optional|
59
60
 
@@ -325,7 +326,7 @@ In hosted mode (recommended), the backend resolves the correct policy automatica
325
326
 
326
327
  ### Fallback: Offline Policies
327
328
 
328
- When no backend is available, `ConsentManagerProvider` accepts `offlinePolicy.policyPacks` for local policy resolution:
329
+ When no backend is available, `ConsentManagerProvider` accepts `offlinePolicy.policyPacks` for local policy resolution during development, testing, previews, or temporary backend outages:
329
330
 
330
331
  ```tsx
331
332
  <ConsentManagerProvider
@@ -376,6 +377,7 @@ When no backend is available, `ConsentManagerProvider` accepts `offlinePolicy.po
376
377
  Notes:
377
378
 
378
379
  * `offlinePolicy` is only used in `offline` mode.
380
+ * Treat offline policies as a development/testing tool or resilience fallback, not the primary production source of truth.
379
381
  * `offlinePolicy.i18n` lets offline mode mirror hosted `messageProfile` and profile-local `fallbackLanguage` behavior.
380
382
  * Omitting `offlinePolicy.policyPacks` uses the built-in synthetic opt-in fallback banner. Hosted network fallback uses the same opt-in banner.
381
383
  * `offlinePolicy: { policyPacks: [] }` is explicit no-banner mode.
@@ -35,22 +35,43 @@ export function PrivacySettingsPage() {
35
35
 
36
36
  Each consent category is rendered as an expandable accordion item. Clicking the category header expands it to show a description and any associated services. Users can toggle individual categories on or off using the switch control. The `necessary` category is always enabled and cannot be toggled.
37
37
 
38
- ## Compound Components
38
+ ## Styling First
39
39
 
40
- Build fully custom widget layouts using sub-components:
40
+ > ℹ️ **Info:**
41
+ > Most widget customization should stay in the stock component. Use theme tokens and slots such as consentWidgetAccordion, consentWidgetFooter, and toggle before reaching for compound components. See Styling Overview.
42
+
43
+ ```tsx
44
+ <ConsentManagerProvider
45
+ options={{
46
+ theme: {
47
+ colors: {
48
+ surface: '#fffdf8',
49
+ surfaceHover: '#f6f3ee',
50
+ },
51
+ slots: {
52
+ consentWidgetAccordion: 'rounded-3xl border border-black/10',
53
+ consentWidgetFooter: 'border-t border-black/10 px-6',
54
+ toggle: 'shadow-sm',
55
+ },
56
+ },
57
+ }}
58
+ >
59
+ <ConsentWidget />
60
+ </ConsentManagerProvider>
61
+ ```
62
+
63
+ Widget copy should be changed through `ConsentManagerProvider.options.i18n` so the inline UI stays aligned with the rest of the consent experience.
64
+
65
+ ## Advanced: Compound Components
66
+
67
+ Use compound components only when you need to rearrange the widget's existing primitives while keeping policy-aware action grouping:
41
68
 
42
69
  ```tsx
43
70
  <ConsentWidget.Root>
44
71
  <ConsentWidget.Accordion type="multiple">
45
72
  <ConsentWidget.AccordionItems />
46
73
  </ConsentWidget.Accordion>
47
- <ConsentWidget.Footer>
48
- <ConsentWidget.FooterSubGroup>
49
- <ConsentWidget.RejectButton />
50
- <ConsentWidget.AcceptAllButton />
51
- </ConsentWidget.FooterSubGroup>
52
- <ConsentWidget.SaveButton />
53
- </ConsentWidget.Footer>
74
+ <ConsentWidget.PolicyActions />
54
75
  </ConsentWidget.Root>
55
76
  ```
56
77
 
@@ -62,12 +83,73 @@ Build fully custom widget layouts using sub-components:
62
83
  * `ConsentWidget.AccordionContent` — Collapsible content area
63
84
  * `ConsentWidget.AccordionArrow` — Expand/collapse indicator
64
85
  * `ConsentWidget.Switch` — Category toggle switch
86
+ * `ConsentWidget.PolicyActions` — Renders grouped policy-aware actions
65
87
  * `ConsentWidget.Footer` — Footer container
66
88
  * `ConsentWidget.FooterSubGroup` — Groups related buttons
67
89
  * `ConsentWidget.AcceptAllButton` — Accepts all consent
68
90
  * `ConsentWidget.RejectButton` — Rejects all consent
69
91
  * `ConsentWidget.SaveButton` — Saves custom selections
70
92
 
93
+ ## Using `renderAction` with c15t Defaults
94
+
95
+ `ConsentWidget.PolicyActions` renders stock c15t buttons and translations by default.
96
+
97
+ ```tsx
98
+ <ConsentWidget.PolicyActions />
99
+ ```
100
+
101
+ `renderAction` is optional. Return the stock button compounds when you want custom mapping while preserving built-in c15t behavior and copy:
102
+
103
+ ```tsx
104
+ <ConsentWidget.PolicyActions
105
+ renderAction={(action, props) => {
106
+ const { key, ...buttonProps } = props
107
+
108
+ switch (action) {
109
+ case 'accept':
110
+ return <ConsentWidget.AcceptAllButton key={key} {...buttonProps} />
111
+ case 'reject':
112
+ return <ConsentWidget.RejectButton key={key} {...buttonProps} />
113
+ case 'customize':
114
+ return <ConsentWidget.SaveButton key={key} {...buttonProps} />
115
+ }
116
+ }}
117
+ />
118
+ ```
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
+ ```
148
+
149
+ For a fixed footer layout, render `ConsentWidget.Footer` and `ConsentWidget.FooterSubGroup` manually instead of using `ConsentWidget.PolicyActions`.
150
+
151
+ If the stock widget structure is already correct, stay with tokens and slots instead of rebuilding the layout.
152
+
71
153
  ## Props
72
154
 
73
155
  ### ConsentWidgetProps
@@ -4,12 +4,12 @@ description: Choose how c15t connects to its backend - full hosted integration,
4
4
  ---
5
5
  c15t supports three client modes that determine how consent data is stored and synchronized. Choose the mode that matches your infrastructure:
6
6
 
7
- * **Hosted mode** - Full backend integration with geolocation, API sync, and analytics
8
- * **Offline mode** - Local-only storage with no network requests
7
+ * **Hosted mode** - Recommended for production. Backend-backed consent with geolocation, centralized policy resolution, audit history, and offline fallback.
8
+ * **Offline mode** - Browser-only storage with no network requests. Best for local development, demos, static deployments, or controlled fallback scenarios.
9
9
  * **Custom mode** - Bring your own backend with custom endpoint handlers
10
10
 
11
11
  > ℹ️ **Info:**
12
- > Offline mode is browser-only storage. If browser storage is blocked or cleared, consent cannot be remembered and prior choices cannot be verified.
12
+ > If you need durable consent records, server-side enforcement, or automatic jurisdiction detection, use hosted mode. Offline mode cannot provide those guarantees because consent lives only in the browser.
13
13
 
14
14
  <span id="c15t-mode" />
15
15
 
@@ -30,6 +30,13 @@ The default mode. Connects to a c15t backend for full consent lifecycle manageme
30
30
 
31
31
  * `backendURL` (required) - API endpoint path
32
32
 
33
+ **Why it is the default for production:**
34
+
35
+ * The backend stays the source of truth for policy, translations, and jurisdiction logic
36
+ * Consent decisions can be stored beyond the current browser session for audit and support workflows
37
+ * Server-side systems can preload consent-aware behavior instead of waiting for client-only storage
38
+ * If the backend is temporarily unavailable, c15t can fall back locally and re-sync later
39
+
33
40
  **Best for:** Production apps that need geolocation-based jurisdiction detection, consent record storage, and compliance audit trails.
34
41
 
35
42
  ```tsx
@@ -78,10 +85,12 @@ If consent is not stored at all (for example, storage is blocked or frequently c
78
85
 
79
86
  * No automatic geolocation or jurisdiction detection
80
87
  * No consent audit trail
88
+ * No centralized policy or translation updates without shipping frontend changes
81
89
  * No cross-device sync
90
+ * No server-side visibility before client initialization
82
91
  * Works without any backend infrastructure
83
92
 
84
- **Best for:** Static sites, development/testing, or as a starting point before setting up a backend.
93
+ **Best for:** Local development, Storybook/static demos, resilience fallback, or simpler sites that explicitly accept browser-only consent storage.
85
94
 
86
95
  ```tsx
87
96
  import { type ReactNode } from 'react';
@@ -156,7 +165,10 @@ export function ConsentManager({ children }: { children: ReactNode }) {
156
165
  |Feature|Hosted|Offline|Custom|
157
166
  |--|--|--|--|
158
167
  |Geolocation|Automatic|Manual via overrides|Your implementation|
168
+ |Policy source of truth|Backend-managed|Bundled into the frontend|Your implementation|
159
169
  |Consent sync|API|Local only|Your implementation|
170
+ |Audit trail|Backend records|Not available|Your implementation|
171
+ |Server-side consent awareness|Supported|Not available|Your implementation|
160
172
  |SSR data|Supported|Not available|Your implementation|
161
173
  |Analytics|Built-in|Not available|Your implementation|
162
174
  |Infrastructure|c15t backend|None|Your backend|
@@ -88,7 +88,7 @@ If the resolved model is `none` or `opt-out` (and `ui.mode` is `none`), consents
88
88
 
89
89
  ## Debugging the Lifecycle
90
90
 
91
- Use the DevTools panel and callbacks to inspect each step of the initialization flow:
91
+ Use the DevTools panel and callbacks to inspect each step of the initialization flow. `onConsentSet` is the broad lifecycle signal; `onConsentChanged` and `subscribeToConsentChanges()` are the change-only signals for explicit post-init saves.
92
92
 
93
93
  |Step|DevTools Panel|Callback|What to check|
94
94
  |--|--|--|--|
@@ -98,6 +98,7 @@ Use the DevTools panel and callbacks to inspect each step of the initialization
98
98
  |Banner visibility|Consents|—|`activeUI` in store state; does policy `ui.mode` require it?|
99
99
  |Re-prompting|Policy|—|Fingerprint mismatch between stored and resolved policy?|
100
100
  |Consent save|Consents + Events|`onConsentSet`|`preferences` object in callback payload|
101
+ |Change-only integrations|Events|`onConsentChanged` or `subscribeToConsentChanges()`|`allowedCategories`, `deniedCategories`, and previous values only when a real save changed preferences|
101
102
  |Script loading|Scripts|`onConsentSet`|Script IDs and their load/blocked status|
102
103
  |Reload on revocation|Events|`onBeforeConsentRevocationReload`|Fires before reload; check localStorage for `c15t:pending-consent-sync`|
103
104
  |Deferred sync|Events|`onError` (if sync fails)|After reload, check Events panel for successful API call|
@@ -120,7 +121,13 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
120
121
  console.log('Init complete:', { jurisdiction, location });
121
122
  },
122
123
  onConsentSet: ({ preferences }) => {
123
- console.log('Consent saved:', preferences);
124
+ console.log('Broad consent lifecycle event:', preferences);
125
+ },
126
+ onConsentChanged: ({ allowedCategories, deniedCategories }) => {
127
+ console.log('Explicit consent change:', {
128
+ allowedCategories,
129
+ deniedCategories,
130
+ });
124
131
  },
125
132
  onBeforeConsentRevocationReload: ({ preferences }) => {
126
133
  console.log('Reloading due to revocation:', preferences);
@@ -10,9 +10,9 @@ There are three ways to configure policy packs:
10
10
 
11
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.
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
- 3. **Offline fallback** — pass the same policy shapes to the frontend via `offlinePolicy.policyPacks`. Used as a resilience fallback when the backend is unreachable, or for quick local experimentation and demos. If you omit `offlinePolicy.policyPacks`, c15t falls back to a synthetic worldwide opt-in banner instead of no-banner mode.
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
 
15
- In both hosted and self-hosted modes, the **backend is always the source of truth**. Offline packs never override a live backend decision.
15
+ In both hosted and self-hosted modes, the **backend is always the source of truth**. Offline packs are a preview or fallback layer and never override a live backend decision.
16
16
 
17
17
  ## Quickstart
18
18
 
package/docs/headless.md CHANGED
@@ -4,11 +4,12 @@ description: Build fully custom consent UI using only hooks - no pre-built compo
4
4
  ---
5
5
  c15t's headless mode means using the hooks (`useConsentManager`, `useTranslations`, etc.) without any pre-built UI components. This gives you complete control over the consent experience.
6
6
 
7
- There are three levels of customization:
7
+ Before you go headless, walk the customization ladder in order:
8
8
 
9
- 1. **Props** - Use pre-built components with custom text and configuration
10
- 2. **noStyle** - Use pre-built components with their structure but strip all styles
11
- 3. **Headless** - Use only hooks and build the entire UI yourself
9
+ 1. **Pre-built components** - Use provider options, component props, tokens, slots, and `theme.consentActions`
10
+ 2. **Compound components** - Rearrange c15t primitives when the markup order must change
11
+ 3. **`noStyle`** - Keep c15t structure but replace its styling
12
+ 4. **Headless** - Use only hooks and build the entire UI yourself
12
13
 
13
14
  ## When to Go Headless
14
15
 
@@ -18,10 +19,15 @@ Go headless when:
18
19
  * You need a consent flow that doesn't fit the banner/dialog pattern
19
20
  * You want to embed consent choices inline rather than as overlays
20
21
 
21
- Use `noStyle` instead when:
22
+ Use a lower-power tool instead when:
22
23
 
23
- * The component structure works but the styling doesn't
24
- * You want to apply your own CSS/Tailwind without fighting defaults
24
+ * The component structure works but the styling doesn't -> use tokens, slots, or `noStyle`
25
+ * You only need to rearrange existing c15t parts -> use compound components
26
+ * You want to change copy -> use `ConsentManagerProvider.options.i18n`
27
+ * You only need to restyle stock actions -> use `theme.consentActions`
28
+
29
+ > ⚠️ **Warning:**
30
+ > Headless mode is not the first answer for pure theming. If you are still trying to debug why a banner footer color did not change, stay in the styling system and verify the token-to-component mapping before you rebuild the UI.
25
31
 
26
32
  > ℹ️ **Info:**
27
33
  > Need a policy-aware implementation guide? See Building Headless Components.
@@ -27,7 +27,7 @@ function MyComponent() {
27
27
 
28
28
  |Property|Type|Description|Default|Required|
29
29
  |:--|:--|:--|:--|:--:|
30
- |branding|"c15t" \|"consent" \|"none"|Whether to show the branding|-|✅ Required|
30
+ |branding|"c15t" \|"inth" \|"consent" \|"none"|Whether to show the branding. "consent" is a deprecated alias for "inth".|-|✅ Required|
31
31
  |consents|ConsentState|Current consent states for all consent types|-|✅ Required|
32
32
  |selectedConsents|ConsentState|Selected consents (Not Saved) - use saveConsents to save|-|✅ Required|
33
33
  |consentInfo|ConsentInfo \|null|Information about when and how consent was given|-|✅ Required|
@@ -53,7 +53,7 @@ function MyComponent() {
53
53
  |iab|IABManager \|null|IAB TCF 2.3 state and actions (null when not configured or not in IAB mode).|-|✅ Required|
54
54
  |reloadOnConsentRevoked|boolean|Whether to reload the page when consent is revoked.|-|✅ Required|
55
55
  |ssrDataUsed|boolean|Whether SSR data was successfully used for initialization.|-|✅ Required|
56
- |ssrSkippedReason|"no\_data" \|"fetch\_failed" \|null|Reason SSR data was skipped, if applicable.|-|✅ Required|
56
+ |ssrSkippedReason|SSRSkippedReason|Reason SSR data was skipped, if applicable.|-|✅ Required|
57
57
 
58
58
  #### `consents` ConsentState
59
59
 
@@ -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|-|-|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|
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|
@@ -196,6 +196,7 @@ IAB TCF 2.3 state and actions (null when not configured or not in IAB mode).
196
196
  |setActiveUI|(ui: ActiveUI, options?: \{ force?: boolean \|undefined; } \|undefined) => void|Sets the active consent UI component.|-|✅ Required|
197
197
  |setConsentCategories|(types: AllConsentNames\[]) => void|Updates the active GDPR consent types.|-|✅ Required|
198
198
  |setCallback|Object \|undefined|Sets a callback for a specific consent event.|-|✅ Required|
199
+ |subscribeToConsentChanges|Object|Subscribes to change-only consent saves.|-|✅ Required|
199
200
  |setLocationInfo|Object \|null|Updates the user's location information.|-|✅ Required|
200
201
  |initConsentManager|Object \|undefined \|null|Initializes the consent manager by fetching jurisdiction, location, translations, and branding information.|-|✅ Required|
201
202
  |getDisplayedConsents|ConsentType|Retrieves the list of consent types that should be displayed|-|✅ Required|
@@ -245,6 +246,19 @@ Identifies the user by setting the external ID.
245
246
  |id|string|Usually your own internal ID for the user from your auth provider|-|✅ Required|
246
247
  |identityProvider|string \|undefined|The identity provider of the user. Usually the name of the identity provider e.g. 'clerk', 'auth0', 'custom', etc.|-|Optional|
247
248
 
249
+ #### `subscribeToConsentChanges`
250
+
251
+ Subscribes to change-only consent saves.
252
+
253
+ |Property|Type|Description|Default|Required|
254
+ |:--|:--|:--|:--|:--:|
255
+ |preferences|ConsentState|-|-|✅ Required|
256
+ |previousPreferences|ConsentState|-|-|✅ Required|
257
+ |allowedCategories|AllConsentNames|-|-|✅ Required|
258
+ |deniedCategories|AllConsentNames|-|-|✅ Required|
259
+ |previousAllowedCategories|AllConsentNames|-|-|✅ Required|
260
+ |previousDeniedCategories|AllConsentNames|-|-|✅ Required|
261
+
248
262
  #### `setLocationInfo`
249
263
 
250
264
  Updates the user's location information.
@@ -25,7 +25,7 @@ function DebugSSR() {
25
25
  |Property|Type|Description|Default|Required|
26
26
  |:--|:--|:--|:--|:--:|
27
27
  |ssrDataUsed|boolean|Whether SSR data was used for initialization. \`true\` if SSR data was provided and successfully consumed, \`false\` otherwise.|-|✅ Required|
28
- |ssrSkippedReason|"no\_data" \|"fetch\_failed" \|null|Reason SSR data was skipped, or \`null\` if used successfully.|-|✅ Required|
28
+ |ssrSkippedReason|"no\_data" \|"fetch\_failed" \|"context\_mismatch" \|null|Reason SSR data was skipped, or \`null\` if used successfully.|-|✅ Required|
29
29
 
30
30
  > ℹ️ **Info:**
31
31
  > Must be used within a ConsentManagerProvider. Throws if used outside the provider context.
@@ -46,6 +46,7 @@ The returned `Translations` object has these sections:
46
46
  |customize|string \|undefined|-|-|✅ Required|
47
47
  |save|string \|undefined|-|-|✅ Required|
48
48
  |close|string \|undefined|-|-|✅ Required|
49
+ |securedBy|string \|undefined|-|-|✅ Required|
49
50
 
50
51
  #### `cookieBanner` CookieBannerTranslations
51
52
 
@@ -19,11 +19,8 @@ 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 {
23
- ConsentManagerProvider,
24
- IABConsentBanner,
25
- IABConsentDialog,
26
- } from '@c15t/nextjs';
22
+ import { ConsentManagerProvider } from '@c15t/nextjs';
23
+ import { IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
27
24
 
28
25
  export default function ConsentManager({ children }: { children: ReactNode }) {
29
26
  return (
@@ -13,11 +13,8 @@ Pair it with `IABConsentBanner` inside the provider:
13
13
 
14
14
  ```tsx
15
15
  import { type ReactNode } from 'react';
16
- import {
17
- ConsentManagerProvider,
18
- IABConsentBanner,
19
- IABConsentDialog,
20
- } from '@c15t/nextjs';
16
+ import { ConsentManagerProvider } from '@c15t/nextjs';
17
+ import { IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
21
18
 
22
19
  export default function ConsentManager({ children }: { children: ReactNode }) {
23
20
  return (
@@ -76,7 +73,7 @@ By default, the dialog follows `activeUI === 'dialog'` from the consent store. U
76
73
 
77
74
  ```tsx
78
75
  import { useState } from 'react';
79
- import { IABConsentDialog } from '@c15t/nextjs';
76
+ import { IABConsentDialog } from '@c15t/react/iab';
80
77
 
81
78
  function SettingsPage() {
82
79
  const [open, setOpen] = useState(false);
@@ -32,15 +32,21 @@ c15t provides a complete IAB TCF 2.3 CMP (Consent Management Platform) implement
32
32
  4. **TC String generation** — Consent choices are encoded into the standard TC String format.
33
33
  5. **`__tcfapi` stub** — The standard CMP API is exposed on `window` so vendor scripts can query consent.
34
34
 
35
+ If you use the prebuilt styled IAB UI, add your framework's `iab/styles.css` entrypoint alongside the base c15t stylesheet. IAB CSS is published separately so apps that do not render IAB surfaces do not ship those component rules.
36
+
35
37
  ## Quick Setup
36
38
 
39
+ If you use the prebuilt styled IAB UI, import the IAB stylesheet alongside the base stylesheet in your global CSS entrypoint:
40
+
41
+ ```css title="src/app/globals.css"
42
+ @import "@c15t/nextjs/styles.css";
43
+ @import "@c15t/nextjs/iab/styles.css";
44
+ ```
45
+
37
46
  ```tsx
38
47
  import { type ReactNode } from 'react';
39
- import {
40
- ConsentManagerProvider,
41
- IABConsentBanner,
42
- IABConsentDialog,
43
- } from '@c15t/nextjs';
48
+ import { ConsentManagerProvider } from '@c15t/nextjs';
49
+ import { IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
44
50
 
45
51
  export default function ConsentManager({ children }: { children: ReactNode }) {
46
52
  return (