@c15t/nextjs 2.2.0 → 3.0.0-alpha.0
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.
- package/AGENTS.md +102 -142
- package/README.md +4 -4
- package/dist/api.js +1 -0
- package/dist/config.js +1 -0
- package/dist/devtools.js +2 -0
- package/dist/headers.js +1 -0
- package/dist/iab/styles.css +1 -1
- package/dist/iab/styles.tw3.css +133 -19
- package/dist/index.js +1 -1
- package/dist/middleware.js +1 -0
- package/dist/node-bridge.js +1 -0
- package/dist/pages.js +1 -0
- package/dist/proxy.js +1 -0
- package/dist/root.js +2 -0
- package/dist/server.js +1 -0
- package/dist/static.js +1 -0
- package/dist/styles.css +1 -1
- package/dist/styles.tw3.css +173 -29
- package/dist/version.js +1 -1
- package/dist-types/api.d.ts +88 -0
- package/dist-types/config.d.ts +110 -0
- package/dist-types/devtools.d.ts +1 -0
- package/dist-types/headers.d.ts +4 -0
- package/dist-types/index.d.ts +28 -10
- package/dist-types/middleware.d.ts +15 -0
- package/dist-types/node-bridge.d.ts +57 -0
- package/dist-types/pages.d.ts +88 -0
- package/dist-types/proxy.d.ts +34 -0
- package/dist-types/root.d.ts +86 -0
- package/dist-types/server.d.ts +164 -0
- package/dist-types/static.d.ts +42 -0
- package/dist-types/types.d.ts +5 -36
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +102 -142
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/customization/overview.md +45 -0
- package/docs/customization/recipes.md +79 -0
- package/docs/customization/slots.md +55 -0
- package/docs/customization/tokens.md +76 -0
- package/docs/customization/translations.md +49 -0
- package/docs/frameworks/next/api-reference/data-fetching.md +416 -0
- package/docs/frameworks/next/app-router.md +403 -0
- package/docs/frameworks/next/client-side.md +118 -0
- package/docs/frameworks/next/components/consent-banner.md +251 -211
- package/docs/frameworks/next/components/consent-dialog-link.md +96 -35
- package/docs/frameworks/next/components/consent-dialog-trigger.md +74 -149
- package/docs/frameworks/next/components/consent-dialog.md +189 -134
- package/docs/frameworks/next/components/consent-manager-provider.md +60 -318
- package/docs/frameworks/next/components/consent-widget.md +172 -114
- package/docs/frameworks/next/components/dev-tools.md +199 -40
- package/docs/frameworks/next/components/frame.md +137 -42
- package/docs/frameworks/next/concepts/consent-categories.md +24 -89
- package/docs/frameworks/next/concepts/policy-presets.md +142 -0
- package/docs/frameworks/next/content-security-policy.md +189 -0
- package/docs/frameworks/next/data-fetching.md +74 -0
- package/docs/frameworks/next/geography-headers.md +251 -0
- package/docs/frameworks/next/headless.md +95 -185
- package/docs/frameworks/next/hooks/use-consent-manager/overview.md +42 -163
- package/docs/frameworks/next/iab/overview.md +37 -107
- package/docs/frameworks/next/optimization.md +158 -194
- package/docs/frameworks/next/pages-router.md +296 -0
- package/docs/frameworks/next/quickstart.md +31 -132
- package/docs/frameworks/next/script-loader.md +134 -467
- package/docs/frameworks/next/server-side.md +97 -130
- package/docs/frameworks/next/static-export.md +164 -0
- package/docs/frameworks/next/styling/overview.md +166 -250
- package/docs/frameworks/next/troubleshooting.md +134 -144
- package/docs/guides/consent-state.md +60 -0
- package/docs/guides/data-fetching.md +163 -0
- package/docs/guides/deployment-modes.md +63 -0
- package/docs/guides/troubleshooting.md +68 -0
- package/docs/guides/verify-consent.md +62 -0
- package/docs/integrations/adobe-analytics.md +239 -105
- package/docs/integrations/ahrefs-analytics.md +238 -104
- package/docs/integrations/amplitude.md +219 -157
- package/docs/integrations/building-integrations.md +32 -224
- package/docs/integrations/clearbit.md +247 -86
- package/docs/integrations/cloudflare-web-analytics.md +250 -84
- package/docs/integrations/crisp.md +251 -97
- package/docs/integrations/databuddy.md +259 -153
- package/docs/integrations/fathom-analytics.md +239 -96
- package/docs/integrations/google-maps.md +328 -207
- package/docs/integrations/google-tag-manager.md +248 -96
- package/docs/integrations/google-tag.md +261 -90
- package/docs/integrations/heap.md +222 -149
- package/docs/integrations/hightouch.md +225 -131
- package/docs/integrations/hotjar.md +239 -90
- package/docs/integrations/intercom.md +239 -98
- package/docs/integrations/linkedin-insights.md +243 -113
- package/docs/integrations/logrocket.md +241 -123
- package/docs/integrations/matomo-analytics.md +256 -111
- package/docs/integrations/meta-pixel.md +197 -324
- package/docs/integrations/microsoft-clarity.md +233 -114
- package/docs/integrations/microsoft-uet.md +245 -110
- package/docs/integrations/mixpanel-analytics.md +252 -87
- package/docs/integrations/openai-pixel.md +441 -0
- package/docs/integrations/overview.md +95 -133
- package/docs/integrations/pirsch.md +249 -96
- package/docs/integrations/plausible-analytics.md +241 -100
- package/docs/integrations/posthog.md +353 -214
- package/docs/integrations/promptwatch.md +251 -81
- package/docs/integrations/reddit-pixel.md +226 -173
- package/docs/integrations/rudderstack.md +244 -187
- package/docs/integrations/rybbit-analytics.md +244 -91
- package/docs/integrations/segment.md +238 -92
- package/docs/integrations/snapchat-pixel.md +240 -110
- package/docs/integrations/tiktok-pixel.md +249 -81
- package/docs/integrations/umami-analytics.md +242 -95
- package/docs/integrations/vercel-analytics.md +242 -90
- package/docs/integrations/x-pixel.md +238 -104
- package/docs/integrations/youtube.md +354 -142
- package/docs/upgrade-v3.md +334 -0
- package/iab/styles.css +1 -1
- package/iab/styles.tw3.css +1 -1
- package/package.json +106 -65
- package/readme.json +3 -3
- package/src/iab/styles.css +1 -1
- package/src/iab/styles.tw3.css +1 -1
- package/src/styles.css +1 -1
- package/src/styles.tw3.css +1 -1
- package/styles.css +1 -1
- package/styles.tw3.css +1 -1
- package/client/components/consent-dialog-link.js +0 -3
- package/client/components/integrations.js +0 -3
- package/dist/components/integrations/index.cjs +0 -1
- package/dist/components/integrations/index.js +0 -1
- package/dist/headless.cjs +0 -1
- package/dist/index.cjs +0 -1
- package/dist/libs/browser-initial-data.cjs +0 -1
- package/dist/libs/browser-initial-data.js +0 -1
- package/dist/libs/initial-data.cjs +0 -1
- package/dist/libs/initial-data.js +0 -1
- package/dist/types.cjs +0 -1
- package/dist/version.cjs +0 -1
- package/dist-types/components/integrations/index.d.ts +0 -1
- package/dist-types/libs/browser-initial-data.d.ts +0 -9
- package/dist-types/libs/initial-data.d.ts +0 -33
- package/docs/frameworks/next/building-headless-components.md +0 -379
- package/docs/frameworks/next/callbacks.md +0 -186
- package/docs/frameworks/next/concepts/client-modes.md +0 -177
- package/docs/frameworks/next/concepts/consent-models.md +0 -117
- package/docs/frameworks/next/concepts/cookie-management.md +0 -122
- package/docs/frameworks/next/concepts/glossary.md +0 -24
- package/docs/frameworks/next/concepts/initialization-flow.md +0 -149
- package/docs/frameworks/next/concepts/policy-packs.md +0 -230
- package/docs/frameworks/next/hooks/use-color-scheme.md +0 -41
- package/docs/frameworks/next/hooks/use-consent-manager/checking-consent.md +0 -96
- package/docs/frameworks/next/hooks/use-consent-manager/location-info.md +0 -97
- package/docs/frameworks/next/hooks/use-consent-manager/setting-consent.md +0 -94
- package/docs/frameworks/next/hooks/use-draggable.md +0 -59
- package/docs/frameworks/next/hooks/use-focus-trap.md +0 -42
- package/docs/frameworks/next/hooks/use-reduced-motion.md +0 -37
- package/docs/frameworks/next/hooks/use-ssr-status.md +0 -32
- package/docs/frameworks/next/hooks/use-text-direction.md +0 -50
- package/docs/frameworks/next/hooks/use-translations.md +0 -55
- package/docs/frameworks/next/iab/consent-banner.md +0 -91
- package/docs/frameworks/next/iab/consent-dialog.md +0 -129
- package/docs/frameworks/next/iab/use-gvl-data.md +0 -21
- package/docs/frameworks/next/iframe-blocking.md +0 -106
- package/docs/frameworks/next/internationalization.md +0 -207
- package/docs/frameworks/next/network-blocker.md +0 -140
- package/docs/frameworks/next/policy-packs.md +0 -248
- package/docs/frameworks/next/styling/classnames.md +0 -94
- package/docs/frameworks/next/styling/color-scheme.md +0 -84
- package/docs/frameworks/next/styling/css-variables.md +0 -53
- package/docs/frameworks/next/styling/slots.md +0 -94
- package/docs/frameworks/next/styling/tailwind.md +0 -137
- package/docs/frameworks/next/styling/tokens.md +0 -156
- package/docs/shared/concepts/client-modes.md +0 -103
- package/docs/shared/concepts/consent-categories.md +0 -41
- package/docs/shared/concepts/consent-models.md +0 -72
- package/docs/shared/concepts/cookie-management.md +0 -88
- package/docs/shared/concepts/glossary.md +0 -24
- package/docs/shared/concepts/initialization-flow.md +0 -105
- package/docs/shared/concepts/policy-packs.md +0 -225
- package/docs/shared/react/components/consent-banner.md +0 -242
- package/docs/shared/react/components/consent-dialog-link.md +0 -45
- package/docs/shared/react/components/consent-dialog-trigger.md +0 -185
- package/docs/shared/react/components/consent-dialog.md +0 -119
- package/docs/shared/react/components/consent-manager-provider.md +0 -225
- package/docs/shared/react/components/consent-widget.md +0 -121
- package/docs/shared/react/components/dev-tools.md +0 -81
- package/docs/shared/react/components/frame.md +0 -52
- package/docs/shared/react/guides/building-headless-components.md +0 -110
- package/docs/shared/react/guides/callbacks.md +0 -89
- package/docs/shared/react/guides/headless.md +0 -31
- package/docs/shared/react/guides/iframe-blocking.md +0 -65
- package/docs/shared/react/guides/internationalization.md +0 -123
- package/docs/shared/react/guides/network-blocker.md +0 -72
- package/docs/shared/react/guides/optimization.md +0 -44
- package/docs/shared/react/guides/policy-packs.md +0 -173
- package/docs/shared/react/guides/script-loader.md +0 -311
- package/docs/shared/react/hooks/use-color-scheme.md +0 -31
- package/docs/shared/react/hooks/use-consent-manager/checking-consent.md +0 -95
- package/docs/shared/react/hooks/use-consent-manager/location-info.md +0 -96
- package/docs/shared/react/hooks/use-consent-manager/overview.md +0 -74
- package/docs/shared/react/hooks/use-consent-manager/setting-consent.md +0 -93
- package/docs/shared/react/hooks/use-draggable.md +0 -30
- package/docs/shared/react/hooks/use-focus-trap.md +0 -20
- package/docs/shared/react/hooks/use-reduced-motion.md +0 -33
- package/docs/shared/react/hooks/use-ssr-status.md +0 -16
- package/docs/shared/react/hooks/use-text-direction.md +0 -38
- package/docs/shared/react/hooks/use-translations.md +0 -15
- package/docs/shared/react/iab/consent-banner.md +0 -60
- package/docs/shared/react/iab/consent-dialog.md +0 -76
- package/docs/shared/react/iab/overview.md +0 -80
- package/docs/shared/react/iab/use-gvl-data.md +0 -21
- package/docs/shared/react/styling/classnames.md +0 -93
- package/docs/shared/react/styling/color-scheme.md +0 -35
- package/docs/shared/react/styling/css-variables.md +0 -53
- package/docs/shared/react/styling/overview.md +0 -261
- package/docs/shared/react/styling/slots.md +0 -93
- package/docs/shared/react/styling/stylesheet-entrypoint.md +0 -8
- package/docs/shared/react/styling/tailwind.md +0 -88
- package/docs/shared/react/styling/tokens.md +0 -155
- package/docs/shared/troubleshooting.md +0 -82
|
@@ -1,138 +1,196 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: ConsentWidget
|
|
3
|
-
description:
|
|
4
|
-
|
|
3
|
+
description: Render ConsentWidget on a Next.js privacy page inside ConsentRoot
|
|
4
|
+
as an inline preference center, server-rendered when the route resolves
|
|
5
|
+
consent.
|
|
5
6
|
group: frameworks
|
|
6
7
|
---
|
|
7
|
-
`ConsentWidget` is a standalone, inline consent management widget. Unlike `ConsentDialog` (which is a modal), the widget embeds directly in your page layout - ideal for privacy settings pages, account preferences, or any page where users should be able to manage consent without a modal overlay.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Embed the preference center inline
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
`ConsentWidget` renders the preference center in the page flow instead of in
|
|
12
|
+
a modal: the category switches and the Reject all, Accept all and Save
|
|
13
|
+
actions, without a title, backdrop or portal. Use it on a privacy or
|
|
14
|
+
settings route where the visitor expects the controls to be part of the
|
|
15
|
+
content. It is a Client Component; render it from any route under the
|
|
16
|
+
layout that mounts the `ConsentRoot` from your
|
|
17
|
+
[App Router](../app-router.md) or
|
|
18
|
+
[Pages Router](../pages-router.md) setup. Keep
|
|
19
|
+
`ConsentDialog` mounted in that root as well so the banner's Customize
|
|
20
|
+
button and preference links still have a dialog to open.
|
|
21
|
+
|
|
22
|
+
```tsx title="app/privacy/page.tsx"
|
|
23
|
+
import { CookiePreferences } from '../../components/cookie-preferences';
|
|
13
24
|
|
|
14
|
-
export function
|
|
25
|
+
export default function PrivacyPage() {
|
|
15
26
|
return (
|
|
16
|
-
|
|
17
|
-
<h1>Privacy
|
|
18
|
-
<p>
|
|
19
|
-
<
|
|
20
|
-
|
|
27
|
+
<main>
|
|
28
|
+
<h1>Privacy</h1>
|
|
29
|
+
<p>Choose which optional categories this site may use.</p>
|
|
30
|
+
<section aria-labelledby="cookie-preferences">
|
|
31
|
+
<h2 id="cookie-preferences">Cookie preferences</h2>
|
|
32
|
+
<CookiePreferences />
|
|
33
|
+
</section>
|
|
34
|
+
</main>
|
|
21
35
|
);
|
|
22
36
|
}
|
|
23
37
|
```
|
|
24
38
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```tsx
|
|
28
|
-
<ConsentWidget
|
|
29
|
-
hideBranding
|
|
30
|
-
legalLinks={['privacyPolicy', 'cookiePolicy']}
|
|
31
|
-
noStyle={false}
|
|
32
|
-
disableAnimation={false}
|
|
33
|
-
/>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Accordion Behavior
|
|
37
|
-
|
|
38
|
-
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.
|
|
39
|
+
```tsx title="components/cookie-preferences.tsx"
|
|
40
|
+
'use client';
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
import { ConsentWidget } from 'c15t/next';
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
<ConsentManagerProvider
|
|
47
|
-
options={{
|
|
48
|
-
theme: {
|
|
49
|
-
colors: {
|
|
50
|
-
surface: '#fffdf8',
|
|
51
|
-
surfaceHover: '#f6f3ee',
|
|
52
|
-
},
|
|
53
|
-
slots: {
|
|
54
|
-
consentWidgetAccordion: 'rounded-3xl border border-black/10',
|
|
55
|
-
consentWidgetFooter: 'border-t border-black/10 px-6',
|
|
56
|
-
consentWidgetFooterSubGroup: 'gap-3',
|
|
57
|
-
toggle: 'shadow-sm',
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
62
|
-
<ConsentWidget />
|
|
63
|
-
</ConsentManagerProvider>
|
|
44
|
+
export function CookiePreferences() {
|
|
45
|
+
return <ConsentWidget />;
|
|
46
|
+
}
|
|
64
47
|
```
|
|
65
48
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
The page stays a Server Component; only the widget file needs
|
|
50
|
+
`'use client'`. On routes where `ConsentRoot` receives the visitor's resolved
|
|
51
|
+
state from an awaited `resolveConsent` (the App Router layout, or a
|
|
52
|
+
Pages Router page with `getServerSideProps`), the widget is part of the server
|
|
53
|
+
HTML with the visitor's recorded choices applied, unlike `ConsentDialog`,
|
|
54
|
+
which mounts after hydration. On a static or otherwise non-prefetched route the
|
|
55
|
+
widget renders after the browser resolves policy. The widget uses the
|
|
56
|
+
boundary's policy, translations and transport; it does not need a second
|
|
57
|
+
provider, and it does not open or close the banner or dialog by itself.
|
|
69
58
|
|
|
70
|
-
##
|
|
71
|
-
|
|
72
|
-
Use compound components only when you need to rearrange the widget's existing primitives while keeping policy-aware action grouping:
|
|
73
|
-
|
|
74
|
-
```tsx
|
|
75
|
-
<ConsentWidget.Root>
|
|
76
|
-
<ConsentWidget.Accordion type="multiple">
|
|
77
|
-
<ConsentWidget.AccordionItems />
|
|
78
|
-
</ConsentWidget.Accordion>
|
|
79
|
-
<ConsentWidget.PolicyActions />
|
|
80
|
-
</ConsentWidget.Root>
|
|
81
|
-
```
|
|
59
|
+
## Props
|
|
82
60
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`
|
|
61
|
+
| Prop | Type | Default | Description |
|
|
62
|
+
| ------------------ | --------- | ---------- | ----------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `hideBranding` | `boolean` | `true` | A standalone widget hides the "Secured by" tag. Pass `false` to show it. |
|
|
64
|
+
| `uiSource` | `string` | `'widget'` | Source identifier recorded with saves made from this widget. Inside `ConsentDialog` it inherits `'dialog'`. |
|
|
65
|
+
| `disableAnimation` | `boolean` | `false` | Skips animations in the widget's parts. |
|
|
66
|
+
| `noStyle` | `boolean` | `false` | Removes the built-in styling from every part. |
|
|
67
|
+
|
|
68
|
+
## Behavior
|
|
69
|
+
|
|
70
|
+
The widget is the preference center without the modal around it: an
|
|
71
|
+
accordion with one row per category, each with a switch, followed by the
|
|
72
|
+
Reject all, Accept all and Save actions. `ConsentDialog` renders this same
|
|
73
|
+
widget inside its card. Standalone, the widget renders in place, takes part
|
|
74
|
+
in server rendering, and does not touch the active surface, so it suits a
|
|
75
|
+
privacy page or an account settings screen.
|
|
76
|
+
|
|
77
|
+
The rows are `necessary` plus the categories in the active policy's scope,
|
|
78
|
+
narrowed to the provider `options.consentCategories` when that list is non-empty.
|
|
79
|
+
The `necessary` switch is on and disabled. Row titles and descriptions come
|
|
80
|
+
from `consentTypes.<category>.title` and `.description`; the action labels
|
|
81
|
+
from `common.rejectAll`, `common.acceptAll` and `common.save`. One row is
|
|
82
|
+
expanded at a time.
|
|
83
|
+
|
|
84
|
+
### Draft and save
|
|
85
|
+
|
|
86
|
+
Switches edit a draft, not the visitor's permissions. Nothing is recorded
|
|
87
|
+
until Save. The draft seeds each category from the recorded choice; without
|
|
88
|
+
one it uses `presentation.preferences.defaults` from the provider options,
|
|
89
|
+
and without that it is on under an opt-out rule and on only for the rule's
|
|
90
|
+
preselected categories under an opt-in rule. Save records the displayed
|
|
91
|
+
categories, then reseeds the draft from the new record. Accept all and
|
|
92
|
+
Reject all record immediately without a separate Save. A save answers a
|
|
93
|
+
choice prompt, so an open choice banner closes; it does not dismiss a
|
|
94
|
+
`notice` prompt, which keeps its own acknowledgement record. Unsaved toggles
|
|
95
|
+
are lost when the widget unmounts.
|
|
96
|
+
|
|
97
|
+
When the policy changes in a way that affects the choice while the draft has
|
|
98
|
+
unsaved toggles, the widget shows an alert, "The privacy policy changed.
|
|
99
|
+
Review the current choices before saving.", with a Review choices button
|
|
100
|
+
that resets the draft. Save is refused until then. A draft without unsaved
|
|
101
|
+
toggles reseeds silently.
|
|
102
|
+
|
|
103
|
+
A saved grant that the current policy or a privacy signal such as Global
|
|
104
|
+
Privacy Control overrides shows a note under its row: "Your saved choice is
|
|
105
|
+
restricted by the current privacy settings." The switch still reflects the
|
|
106
|
+
saved value; the effective permission is off.
|
|
107
|
+
|
|
108
|
+
### Actions
|
|
109
|
+
|
|
110
|
+
The preference center always renders Reject all, Accept all and Save, with
|
|
111
|
+
Save as the primary action. `presentation.preferences.layout`,
|
|
112
|
+
`primaryActions`, `direction` and `uiProfile` in the provider options
|
|
113
|
+
reorder and restyle them; a layout that omits one of the three has it
|
|
114
|
+
restored. Customize and dismiss actions never render here.
|
|
115
|
+
|
|
116
|
+
### When consent is not owed
|
|
117
|
+
|
|
118
|
+
Without a resolved policy rule the widget renders nothing and appears as
|
|
119
|
+
soon as a rule resolves, without a remount. A rule with `model: 'none'` and
|
|
120
|
+
an empty `rights` list renders nothing; a `none` rule that lists any right,
|
|
121
|
+
such as `['disclosure']`, renders the widget and Save completes without
|
|
122
|
+
writing a consent record.
|
|
123
|
+
|
|
124
|
+
For a fully custom preference center, `useConsentDraft()` returns the same
|
|
125
|
+
draft: `values`, `displayedCategories`, `isDirty`, `isStale`, `set`,
|
|
126
|
+
`update`, `acceptAll`, `rejectAll`, `save` and `reset`.
|
|
127
|
+
|
|
128
|
+
The `useConsentDraft` hook and `ConsentDraftProvider` component import from
|
|
129
|
+
`c15t/next`. Use them in Client Components.
|
|
130
|
+
|
|
131
|
+
## Accessibility
|
|
132
|
+
|
|
133
|
+
Each row is a disclosure: a button that expands the category description
|
|
134
|
+
and a switch beside it, so a visitor can read the description without
|
|
135
|
+
changing the choice. Each switch has the category title as its accessible
|
|
136
|
+
name. The policy-change alert uses `role="alert"`, and a restriction note is
|
|
137
|
+
an `output` element linked to its switch through `aria-describedby`. The
|
|
138
|
+
root's `dir` attribute follows the active language.
|
|
139
|
+
|
|
140
|
+
## Composition
|
|
141
|
+
|
|
142
|
+
Every part is available as `ConsentWidget.<Part>`. `Root` provides the
|
|
143
|
+
draft and accepts `noStyle`, `disableAnimation` and `uiSource`. `Accordion`
|
|
144
|
+
and `AccordionItems` render the stock rows; `AccordionItem`,
|
|
145
|
+
`AccordionTrigger`, `AccordionTriggerInner`, `AccordionContent`,
|
|
146
|
+
`AccordionArrow` and `Switch` build your own. `PolicyActions` renders the
|
|
147
|
+
resolved actions in `Footer` and `FooterSubGroup` and accepts `renderAction`
|
|
148
|
+
to replace one button; `AcceptAllButton`, `RejectButton`, `SaveButton` and
|
|
149
|
+
`CustomizeButton` are the individual buttons.
|
|
150
|
+
|
|
151
|
+
`Accordion` is controlled: pass `value` and `onValueChange`, or no
|
|
152
|
+
category description ever opens. The stock `ConsentWidget` holds that state
|
|
153
|
+
for you. In this component, `ConsentWidget` comes from the same import as
|
|
154
|
+
the example at the top of the page:
|
|
101
155
|
|
|
102
156
|
```tsx
|
|
103
|
-
|
|
104
|
-
```
|
|
157
|
+
import { useState } from 'react';
|
|
105
158
|
|
|
106
|
-
|
|
159
|
+
export function PreferenceCenter() {
|
|
160
|
+
const [open, setOpen] = useState<string[]>([]);
|
|
107
161
|
|
|
108
|
-
|
|
109
|
-
<ConsentWidget.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
}}
|
|
122
|
-
/>
|
|
162
|
+
return (
|
|
163
|
+
<ConsentWidget.Root>
|
|
164
|
+
<ConsentWidget.Accordion
|
|
165
|
+
value={open}
|
|
166
|
+
onValueChange={(next) => setOpen(Array.isArray(next) ? next : [next])}
|
|
167
|
+
>
|
|
168
|
+
<ConsentWidget.AccordionItems />
|
|
169
|
+
</ConsentWidget.Accordion>
|
|
170
|
+
<ConsentWidget.PolicyActions />
|
|
171
|
+
</ConsentWidget.Root>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
123
174
|
```
|
|
124
175
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
176
|
+
Provider component slots for the stock structure are `manager.root`,
|
|
177
|
+
`manager.footer`, `manager.actionGroup`, `accordion.root`,
|
|
178
|
+
`accordion.triggerRow`, `accordion.title`, `accordion.control`,
|
|
179
|
+
`accordion-item.root`, `accordion-item.trigger`, `accordion-item.content`
|
|
180
|
+
and `tag.manager`.
|
|
181
|
+
|
|
182
|
+
The compound parts import from `c15t/next` as properties of `ConsentWidget`.
|
|
183
|
+
Server Components cannot render them: keep the composition in a file with
|
|
184
|
+
`'use client'`. See [Styling](../styling/overview.md) for slots and tokens.
|
|
185
|
+
|
|
186
|
+
## Verify
|
|
187
|
+
|
|
188
|
+
Visit the page with the widget. Use a displayed optional category whose permission is
|
|
189
|
+
not restricted by policy or privacy signals such as GPC. One row per displayed
|
|
190
|
+
category appears, with `necessary` on and disabled. Turn that category on:
|
|
191
|
+
`useConsent('<category>')`
|
|
192
|
+
elsewhere on the page still reports the old value. Choose Save: it now
|
|
193
|
+
reports `true`. An open choice banner closes when the save answers its
|
|
194
|
+
prompt; a notice banner stays open until acknowledged. Reload the page: the
|
|
195
|
+
switch keeps the saved value. Without a saved choice or configured draft
|
|
196
|
+
defaults, optional switches start on under an opt-out rule.
|
|
@@ -1,64 +1,224 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: DevTools
|
|
3
|
-
description:
|
|
4
|
-
|
|
3
|
+
description: Load the c15t DevTools panel only in Next.js development builds to
|
|
4
|
+
inspect consent state, scripts, policy and events inside ConsentRoot.
|
|
5
5
|
group: frameworks
|
|
6
6
|
---
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
`DevTools` is a floating panel for the c15t v3 kernel. Use it during development to inspect consent values, scripts, location, the resolved policy, IAB state, events, and consent actions. The framework adapter reads the kernel from the nearest v3 provider. It never discovers a store through a window global.
|
|
8
9
|
|
|
9
10
|
> ⚠️ **Warning:**
|
|
10
|
-
> DevTools
|
|
11
|
+
> Load DevTools only in development. A conditional JSX expression stops it from mounting in production, but a static import can still add DevTools to the production bundle. Use a development-only dynamic import as shown below. The component renders nothing into the React tree and mounts its panel in document.body.
|
|
11
12
|
|
|
12
13
|
## Installation
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```
|
|
15
|
+
No extra package is required for a framework adapter. The adapter and its
|
|
16
|
+
engine are included with the `c15t` package and the dedicated
|
|
17
|
+
`@c15t/svelte` adapter. Install `@c15t/dev-tools` directly only
|
|
18
|
+
when you need the imperative `createDevTools({ kernel })` API.
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import { DevTools } from '@c15t/dev-tools/react';
|
|
22
|
+
Keep the `ConsentRoot` from your router setup. Add this client component as
|
|
23
|
+
one of its children, alongside the banner and dialog. It uses the existing
|
|
24
|
+
runtime, including its prefetch result and configured transport.
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
```tsx title="components/consent-dev-tools.tsx"
|
|
27
|
+
'use client';
|
|
28
|
+
|
|
29
|
+
import dynamic from 'next/dynamic';
|
|
30
|
+
|
|
31
|
+
const DevTools =
|
|
32
|
+
process.env.NODE_ENV === 'development'
|
|
33
|
+
? dynamic(() => import('c15t/next/devtools').then(({ DevTools }) => DevTools), {
|
|
34
|
+
ssr: false,
|
|
35
|
+
})
|
|
36
|
+
: () => null;
|
|
37
|
+
|
|
38
|
+
export function ConsentDevTools() {
|
|
39
|
+
return <DevTools />;
|
|
34
40
|
}
|
|
35
41
|
```
|
|
36
42
|
|
|
43
|
+
Import `ConsentDevTools` into your existing consent wrapper or App Router layout
|
|
44
|
+
and render `<ConsentDevTools />` inside `ConsentRoot`. Keep its `state`,
|
|
45
|
+
`config` and other props unchanged. A setup that uses `ConsentProvider`
|
|
46
|
+
directly can place the same component inside that provider.
|
|
47
|
+
|
|
48
|
+
The umbrella import is `c15t/next/devtools`. When installing the dedicated
|
|
49
|
+
Next.js package, use `@c15t/nextjs/devtools`.
|
|
50
|
+
|
|
37
51
|
## Configuration
|
|
38
52
|
|
|
39
53
|
```tsx
|
|
40
54
|
<DevTools
|
|
41
|
-
position="bottom-right"
|
|
42
|
-
defaultOpen={false}
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
position="bottom-right"
|
|
56
|
+
defaultOpen={false}
|
|
57
|
+
defaultTab="consents"
|
|
58
|
+
maxEvents={100}
|
|
59
|
+
disabled={false}
|
|
45
60
|
/>
|
|
46
61
|
```
|
|
47
62
|
|
|
63
|
+
`position` accepts any corner: `top-left`, `top-right`, `bottom-left`, or
|
|
64
|
+
`bottom-right`. Set `defaultOpen` to open the initial panel and `defaultTab`
|
|
65
|
+
to choose that panel. `maxEvents` limits captured kernel and script events.
|
|
66
|
+
Set `disabled` to prevent mounting without removing the component.
|
|
67
|
+
|
|
48
68
|
## Panels
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
70
|
+
Accept and reject apply only to categories displayed by the provider, leaving
|
|
71
|
+
hidden consent values unchanged. `necessary` always remains enabled. Svelte
|
|
72
|
+
and Vue adapters use their provider's displayed categories; the kernel and
|
|
73
|
+
React adapter use the current policy categories by default.
|
|
74
|
+
|
|
75
|
+
For an imperative integration with a narrower UI, pass
|
|
76
|
+
`getConsentCategories: () => ['necessary', 'measurement']` to
|
|
77
|
+
`createDevTools`. The getter is read again when an action runs. Headless UI
|
|
78
|
+
actions can use `kernel.commands.save('all', { categories: displayed })` or
|
|
79
|
+
`kernel.commands.save('none', { categories: displayed })`. These scoped bulk
|
|
80
|
+
saves use a `custom` transport action when they cover only part of the policy.
|
|
81
|
+
Actions covering the whole policy retain `all` or `necessary`. Without an
|
|
82
|
+
explicit scope or policy categories, bulk saves cover all known categories.
|
|
83
|
+
|
|
84
|
+
| Panel | What it shows |
|
|
85
|
+
| ------------ | -------------------------------------------------------------------------------------------- |
|
|
86
|
+
| **Consents** | Inspect and save categories, accept all, or reject optional categories |
|
|
87
|
+
| **Scripts** | Configured scripts, loading status, search, and external page resources |
|
|
88
|
+
| **Location** | Detected jurisdiction, country, region, and consent model |
|
|
89
|
+
| **Policy** | Resolved policy and UI configuration |
|
|
90
|
+
| **IAB** | Edit vendors, purposes, legitimate interests, and special features; save and copy TC strings |
|
|
91
|
+
| **Events** | Timeline of consent changes, kernel events, and script lifecycle events |
|
|
92
|
+
| **Actions** | Show the banner, open preferences, hide consent UI, or refresh consent data |
|
|
93
|
+
|
|
94
|
+
### Script inspection
|
|
95
|
+
|
|
96
|
+
Set `defaultTab="scripts"` to open script inspection first. It reads all script
|
|
97
|
+
loaders attached to the current provider's kernel, including loaders created
|
|
98
|
+
before DevTools mounts. Search by script ID, category, URL, or status, then
|
|
99
|
+
expand a script to inspect its configuration and latest lifecycle event.
|
|
100
|
+
|
|
101
|
+
`loading` means an external script was inserted; `loaded` means its browser
|
|
102
|
+
load event fired, or an inline script or callback-only integration mounted.
|
|
103
|
+
`error` reports a loader error. `blocked` means consent requirements were not
|
|
104
|
+
met, and `pending` means an eligible script has not mounted. `present` means
|
|
105
|
+
the loader reused an element without a confirmed load result. `retained`
|
|
106
|
+
means consent was revoked but `persistAfterConsentRevoked` kept the element.
|
|
107
|
+
Retained scripts still receive `onConsentChange` with `hasConsent: false`, so
|
|
108
|
+
their integrations can send the vendor's consent-revocation command.
|
|
109
|
+
`alwaysLoad` bypasses the loading gate, not consent: callbacks receive the
|
|
110
|
+
actual consent state and updates across categories for integrations such as
|
|
111
|
+
Google Consent Mode.
|
|
112
|
+
|
|
113
|
+
Script details distinguish `allowedToLoad` from `consentGranted`. An
|
|
114
|
+
`alwaysLoad` integration can be allowed to load while its consent is denied.
|
|
115
|
+
Diagnostics expose these as `eligible` and `hasConsent`, respectively.
|
|
116
|
+
|
|
117
|
+
Lifecycle diagnostics work even when legacy debug forwarding is disabled.
|
|
118
|
+
The page scan lists external scripts and iframes present in the
|
|
119
|
+
DOM; it does not prove that they loaded successfully or were consent-gated.
|
|
120
|
+
|
|
121
|
+
For custom inspection tools, import `getScriptDiagnostics(kernel)` and
|
|
122
|
+
`subscribeScriptDiagnostics(kernel, listener)` from
|
|
123
|
+
`c15t/modules/script-loader`. The subscription reports loader
|
|
124
|
+
registration, updates, disposal, and lifecycle events. Read a fresh snapshot
|
|
125
|
+
after changes and call the returned unsubscribe function during cleanup.
|
|
126
|
+
|
|
127
|
+
### IAB editing
|
|
128
|
+
|
|
129
|
+
Under an IAB policy, the Consents tab is read-only. Edit and save vendors
|
|
130
|
+
and purposes in the IAB tab so the derived categories and TC string stay
|
|
131
|
+
consistent.
|
|
132
|
+
|
|
133
|
+
The IAB panel connects to the existing `@c15t/iab` module attached to the
|
|
134
|
+
provider's kernel. It does not create a second CMP or replace `__tcfapi`.
|
|
135
|
+
Controls become available after initialization, when the current policy uses
|
|
136
|
+
IAB and the vendor list is loaded.
|
|
137
|
+
|
|
138
|
+
Choose Vendors, Purposes, or Special features, then search by name or ID.
|
|
139
|
+
Vendors include the provider's custom vendors. Long lists show 20 entries per
|
|
140
|
+
page. Legitimate-interest controls appear for declared legitimate interests.
|
|
141
|
+
Search only filters the view; Accept all IAB and Reject all IAB apply to the
|
|
142
|
+
configured choices, including custom vendors.
|
|
143
|
+
|
|
144
|
+
Toggles update live IAB state and script gating immediately. Use Save IAB
|
|
145
|
+
consent to generate a fresh TC string and run the configured save transport.
|
|
146
|
+
The panel displays the last generated string; unsaved edits are not represented
|
|
147
|
+
in it. Copy TC string confirms success or reports clipboard failures. Raw IAB
|
|
148
|
+
data remains available in an expandable section.
|
|
149
|
+
|
|
150
|
+
IAB saves normally write the TC string to its standard cookie and localStorage.
|
|
151
|
+
For an in-memory playground, set `persistence: false` in the IAB module options
|
|
152
|
+
as well as disabling the provider's core persistence. The IAB option prevents
|
|
153
|
+
TC-string storage writes; it does not disable the configured save transport
|
|
154
|
+
or remove previously stored values.
|
|
155
|
+
|
|
156
|
+
Save and refresh actions show pending, success, and failure feedback. Controls
|
|
157
|
+
are disabled while a request is pending. A failed save does not roll back the
|
|
158
|
+
live choices; retry to record them.
|
|
159
|
+
|
|
160
|
+
For custom inspection tools, `getIABControls(kernel)` and
|
|
161
|
+
`subscribeIABControls(kernel, listener)` are exported from `c15t`.
|
|
162
|
+
The getter returns undefined before module initialization and after disposal.
|
|
163
|
+
A snapshot without an attached IAB module is read-only in DevTools.
|
|
164
|
+
|
|
165
|
+
## Vue and Svelte
|
|
166
|
+
|
|
167
|
+
Vue imports `ConsentDevTools` from `c15t/vue/devtools`.
|
|
168
|
+
Render it in the app where the c15t plugin provides
|
|
169
|
+
the kernel. In Nuxt, the configured c15t module provides that context.
|
|
170
|
+
|
|
171
|
+
```vue
|
|
172
|
+
<script setup lang="ts">
|
|
173
|
+
import { defineAsyncComponent } from 'vue';
|
|
174
|
+
|
|
175
|
+
const DevTools = import.meta.env.DEV
|
|
176
|
+
? defineAsyncComponent(() => import('c15t/vue/devtools'))
|
|
177
|
+
: null;
|
|
178
|
+
</script>
|
|
179
|
+
|
|
180
|
+
<template>
|
|
181
|
+
<DevTools v-if="DevTools" position="bottom-right" />
|
|
182
|
+
</template>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
In Nuxt, use `import.meta.dev` instead of `import.meta.env.DEV`.
|
|
186
|
+
Svelte imports the adapter from `@c15t/svelte/devtools` and renders it
|
|
187
|
+
inside `ConsentManagerProvider`.
|
|
188
|
+
|
|
189
|
+
```svelte
|
|
190
|
+
<script lang="ts">
|
|
191
|
+
import { ConsentManagerProvider, offline } from '@c15t/svelte';
|
|
192
|
+
|
|
193
|
+
const devTools = import.meta.env.DEV
|
|
194
|
+
? import('@c15t/svelte/devtools')
|
|
195
|
+
: null;
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
<ConsentManagerProvider options={{ mode: offline() }}>
|
|
199
|
+
{#if devTools}
|
|
200
|
+
{#await devTools then { ConsentDevTools }}
|
|
201
|
+
<ConsentDevTools position="bottom-right" />
|
|
202
|
+
{/await}
|
|
203
|
+
{/if}
|
|
204
|
+
</ConsentManagerProvider>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Both adapters accept `position`, `defaultOpen`, `defaultTab`, `maxEvents`,
|
|
208
|
+
`shadow` (`false` renders into the page instead of a shadow root),
|
|
209
|
+
and `getConsentCategories`. Changing these presentation options recreates
|
|
210
|
+
the panel and clears its event history. They do not accept React's `disabled`
|
|
211
|
+
prop; conditionally render them instead. Unmounting removes the panel and
|
|
212
|
+
subscriptions without changing consent.
|
|
58
213
|
|
|
59
214
|
## TanStack Devtools
|
|
60
215
|
|
|
61
|
-
|
|
216
|
+
Each embedded panel owns its event history and subscriptions. Unmounting it
|
|
217
|
+
destroys the instance; remounting starts a new history. If you need to capture
|
|
218
|
+
events while switching plugins, keep the c15t panel mounted. This differs from
|
|
219
|
+
the old globally shared DevTools store.
|
|
220
|
+
|
|
221
|
+
The React v3 DevTools adapter exports a panel component and plugin factory that match TanStack Devtools' plugin API:
|
|
62
222
|
|
|
63
223
|
```tsx
|
|
64
224
|
import * as React from 'react';
|
|
@@ -66,7 +226,7 @@ import { useRouter } from '@tanstack/react-router';
|
|
|
66
226
|
import { TanStackDevtools } from '@tanstack/react-devtools';
|
|
67
227
|
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
|
|
68
228
|
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
|
|
69
|
-
import { c15tDevtools } from '
|
|
229
|
+
import { c15tDevtools } from 'c15t/react/devtools';
|
|
70
230
|
|
|
71
231
|
export function AppDevtools() {
|
|
72
232
|
const router = useRouter();
|
|
@@ -89,11 +249,10 @@ export function AppDevtools() {
|
|
|
89
249
|
}
|
|
90
250
|
```
|
|
91
251
|
|
|
92
|
-
|
|
252
|
+
In a Next.js app, import `c15tDevtools` from `c15t/next/devtools` instead. Scoped installs use `@c15t/react/devtools` and `@c15t/nextjs/devtools`.
|
|
93
253
|
|
|
94
|
-
|
|
95
|
-
|:--|:--|
|
|
96
|
-
|Type Name|\`C15TDevToolsProps\`|
|
|
97
|
-
|Source Path|\`./packages/dev-tools/src/react.ts\`|
|
|
254
|
+
## Props
|
|
98
255
|
|
|
99
|
-
|
|
256
|
+
| Property | Type | Description | Default | Required |
|
|
257
|
+
| :------- | :------------------ | :------------------------------------------ | :------ | :------: |
|
|
258
|
+
| disabled | boolean \|undefined | Prevents the DevTools engine from mounting. | false | Optional |
|