@c15t/react 3.0.0-alpha.5 → 3.0.0-alpha.6
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 +6 -4
- package/dist/component-hooks/use-headless-consent-ui.js +1 -1
- package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
- package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
- package/dist/components/iab-panel/index.js +1 -1
- package/dist/components/panel/index.js +1 -1
- package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
- package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -1
- package/dist/components/panel-trigger/panel-trigger.js +1 -1
- package/dist/components/preferences/index.js +1 -1
- package/dist/components/shared/ui/accordion/accordion.js +1 -1
- package/dist/components/shared/ui/collapsible/collapsible.js +1 -1
- package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
- package/dist/components/shared/ui/switch/switch.js +1 -1
- package/dist/components/shared/ui/tabs/tabs.js +1 -1
- package/dist/devtools-launcher.js +2 -0
- package/dist/devtools.js +1 -1
- package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
- package/dist/index-parts/core.js +1 -1
- package/dist/provider.js +1 -1
- package/dist/styles.tw3.css +27 -3
- package/dist/version.js +1 -1
- package/dist-types/component-hooks/use-headless-consent-ui.d.ts +4 -4
- package/dist-types/components/iab-panel/index.d.ts +0 -1
- package/dist-types/components/panel/index.d.ts +0 -1
- package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +5 -0
- package/dist-types/components/panel-trigger/panel-trigger.d.ts +0 -8
- package/dist-types/components/preferences/index.d.ts +0 -1
- package/dist-types/components/shared/ui/accordion/accordion.d.ts +0 -1
- package/dist-types/components/shared/ui/collapsible/collapsible.d.ts +0 -1
- package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +0 -1
- package/dist-types/components/shared/ui/switch/switch.d.ts +0 -1
- package/dist-types/components/shared/ui/tabs/tabs.d.ts +0 -1
- package/dist-types/devtools-launcher.d.ts +29 -0
- package/dist-types/hooks/use-iab-manager.d.ts +4 -4
- package/dist-types/hooks.d.ts +27 -5
- package/dist-types/index-parts/core.d.ts +1 -1
- package/dist-types/index.d.ts +1 -1
- package/dist-types/provider.d.ts +12 -1
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +6 -4
- package/docs/concepts/choose-your-setup.md +22 -5
- package/docs/concepts/consent-state.md +41 -0
- package/docs/concepts/data-fetching.md +34 -11
- package/docs/customization/recipes.md +4 -5
- package/docs/customization/slots.md +3 -2
- package/docs/customization/stylesheets.md +29 -39
- package/docs/customization/tailwind.md +5 -5
- package/docs/customization/tokens.md +1 -1
- package/docs/customization/translations.md +32 -5
- package/docs/frameworks/react/components/consent-dialog-trigger.md +5 -0
- package/docs/frameworks/react/components/consent-dialog.md +2 -1
- package/docs/frameworks/react/components/dev-tools.md +23 -0
- package/docs/frameworks/react/headless.md +4 -4
- package/docs/frameworks/react/hooks.md +10 -5
- package/docs/frameworks/react/iab.md +24 -18
- package/docs/frameworks/react/quickstart.md +77 -16
- package/docs/frameworks/react/rendering.md +82 -11
- package/docs/frameworks/react/scripts.md +8 -3
- package/docs/frameworks/react/upgrade-v3.md +596 -0
- package/docs/frameworks/react/vendor-consent.md +6 -0
- package/docs/guides/banner-experiments.md +26 -5
- package/docs/guides/troubleshooting.md +3 -2
- package/docs/integrations/overview.md +28 -27
- package/docs/integrations/sentry.md +416 -0
- package/docs/upgrade-v3.md +201 -314
- package/package.json +8 -8
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Upgrade from v2
|
|
3
|
+
description: Upgrade a React app from c15t v2 (@c15t/react) to v3. Covers
|
|
4
|
+
packages, ConsentProvider and transports, the useConsentManager codemod,
|
|
5
|
+
callbacks, policies, styles, IAB and stored consent.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Hand the upgrade to a coding agent
|
|
10
|
+
|
|
11
|
+
**Upgrade this React app to c15t v3**
|
|
12
|
+
|
|
13
|
+
Paste into Claude Code, Codex, Cursor or another coding agent at the root of your app.
|
|
14
|
+
|
|
15
|
+
```prompt
|
|
16
|
+
Upgrade this React app from c15t v2 to c15t v3.
|
|
17
|
+
|
|
18
|
+
Read <https://c15t.com/docs/frameworks/react/upgrade-v3.md> first and follow it in order. Do not guess v3 APIs from memory.
|
|
19
|
+
|
|
20
|
+
1. List every c15t dependency and import: `@c15t/react`, `@c15t/scripts`, `@c15t/dev-tools` and `c15t`.
|
|
21
|
+
2. Check that React is 18 or later. Stop and tell me if it needs a major upgrade.
|
|
22
|
+
3. Replace the packages with `c15t@alpha`, plus `@c15t/integrations@alpha` if the app uses vendor helpers.
|
|
23
|
+
4. Run `npx @c15t/cli@alpha codemods consent-provider-options root-exports-to-subpaths use-consent-manager-to-hooks scripts-to-integrations dev-tools-to-c15t policy-packs-to-policy-rules callbacks-to-v3 theme-to-consent-theme iab-option-to-iab-provider css-variables-to-v3 postcss-tailwind3 --dry-run --json`. Review the output, then run the same command without `--dry-run`. The codemods leave a `TODO(c15t v3)` comment wherever they need you to finish a change.
|
|
24
|
+
5. Check that `ConsentManagerProvider` is now `ConsentProvider` from `c15t/react`, with a transport such as `hosted()` as `mode`. Finish any custom mode the codemod marked.
|
|
25
|
+
6. Update callbacks, policy presets, renamed options and components, theme CSS, CSS variables and IAB setup as the guide describes.
|
|
26
|
+
7. Keep the existing backend URL. If the app runs its own c15t backend, stop and tell me, because it has to be upgraded in the same release.
|
|
27
|
+
8. Run the typecheck and build, and resolve every `TODO(c15t v3)` comment.
|
|
28
|
+
|
|
29
|
+
When you finish, list the files you changed, anything you could not migrate, and how you checked that vendor requests still wait for consent.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Before you start
|
|
33
|
+
|
|
34
|
+
* Upgrade React and React DOM to 18 or later.
|
|
35
|
+
* If you run your own c15t backend, upgrade it in the same release. A v3
|
|
36
|
+
client can't read a v2 backend. See
|
|
37
|
+
[Upgrade a self-hosted backend](../../upgrade-v3.md#upgrade-a-self-hosted-backend).
|
|
38
|
+
Inth-hosted backends need no change.
|
|
39
|
+
|
|
40
|
+
## Replace the packages
|
|
41
|
+
|
|
42
|
+
Remove `@c15t/react` and `@c15t/scripts`, then install `c15t` from the
|
|
43
|
+
`alpha` dist-tag. npm's default tag still resolves v2.
|
|
44
|
+
|
|
45
|
+
| Package manager | Command |
|
|
46
|
+
| :-------------- | :------------------------------------------------ |
|
|
47
|
+
| npm | `npm install c15t@alpha @c15t/integrations@alpha` |
|
|
48
|
+
| pnpm | `pnpm add c15t@alpha @c15t/integrations@alpha` |
|
|
49
|
+
| yarn | `yarn add c15t@alpha @c15t/integrations@alpha` |
|
|
50
|
+
| bun | `bun add c15t@alpha @c15t/integrations@alpha` |
|
|
51
|
+
|
|
52
|
+
Drop `@c15t/integrations@alpha` if you don't use its vendor helpers.
|
|
53
|
+
|
|
54
|
+
| v2 import | v3 import |
|
|
55
|
+
| --------------------------------------- | ------------------------------------- |
|
|
56
|
+
| `@c15t/react` | `c15t/react` |
|
|
57
|
+
| `@c15t/react/headless` | `c15t/react/headless` |
|
|
58
|
+
| `@c15t/react/styles.css` | `c15t/react/styles.css` |
|
|
59
|
+
| `DevTools` from `@c15t/dev-tools/react` | `DevTools` from `c15t/react/devtools` |
|
|
60
|
+
| `@c15t/scripts/*` | `@c15t/integrations/*` |
|
|
61
|
+
|
|
62
|
+
`@c15t/react` is still published at v3, so its imports keep working if you pin
|
|
63
|
+
it to `@alpha`. These docs use `c15t/react`.
|
|
64
|
+
|
|
65
|
+
Every v3 package ships ESM only. If a CommonJS file in your app calls
|
|
66
|
+
`require()` on a c15t package, convert it to ESM.
|
|
67
|
+
|
|
68
|
+
## Rename the integrations dependency
|
|
69
|
+
|
|
70
|
+
v3 renames `@c15t/scripts` to `@c15t/integrations`. Remove `@c15t/scripts`
|
|
71
|
+
from your dependencies and install `@c15t/integrations` from the `alpha`
|
|
72
|
+
dist-tag:
|
|
73
|
+
|
|
74
|
+
| Package manager | Command |
|
|
75
|
+
| :-------------- | :------------------------------------- |
|
|
76
|
+
| npm | `npm install @c15t/integrations@alpha` |
|
|
77
|
+
| pnpm | `pnpm add @c15t/integrations@alpha` |
|
|
78
|
+
| yarn | `yarn add @c15t/integrations@alpha` |
|
|
79
|
+
| bun | `bun add @c15t/integrations@alpha` |
|
|
80
|
+
|
|
81
|
+
Replace the package name in each import. Vendor subpaths and helper names stay
|
|
82
|
+
the same.
|
|
83
|
+
|
|
84
|
+
Before, in v2:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { googleTagManager } from '@c15t/scripts/google-tag-manager';
|
|
88
|
+
import { posthog } from '@c15t/scripts/posthog';
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
After:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { googleTagManager } from '@c15t/integrations/google-tag-manager';
|
|
95
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The `scripts` configuration option and the `Script` type keep their names.
|
|
99
|
+
|
|
100
|
+
Two helper behaviors change. A helper that needs an ID, such as `metaPixel`,
|
|
101
|
+
`googleTagManager` or `posthog`, now throws when the ID is empty or only
|
|
102
|
+
whitespace, where v2 loaded a broken script. And if your code reads
|
|
103
|
+
`@c15t/integrations/registry`, an entry's `consentCategory` can now be a
|
|
104
|
+
compound condition such as `{ and: ['marketing', 'measurement'] }`, not only a
|
|
105
|
+
category name.
|
|
106
|
+
|
|
107
|
+
`@c15t/scripts` stays available as a deprecated compatibility package for all
|
|
108
|
+
of v3. It re-exports the implementation and types of `@c15t/integrations`, so
|
|
109
|
+
you can migrate imports separately from the rest of the upgrade. Compatibility
|
|
110
|
+
ends in v4; versions already published stay on npm.
|
|
111
|
+
|
|
112
|
+
To preview the import changes in JavaScript and TypeScript files:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npx @c15t/cli@alpha codemods scripts-to-integrations --dry-run --json
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Review the output, then run it again without `--dry-run`. The codemod runs
|
|
119
|
+
only when you name it. It does not change `package.json`, lockfiles, or
|
|
120
|
+
imports inside `.vue`, `.svelte` or `.astro` files; update those by hand, then
|
|
121
|
+
reinstall dependencies and build the app.
|
|
122
|
+
|
|
123
|
+
## Replace `useConsentManager()`
|
|
124
|
+
|
|
125
|
+
`useConsentManager()` returned the whole consent state, so every component
|
|
126
|
+
that called it re-rendered on every change. v3 has one hook per field. Run the
|
|
127
|
+
codemod first:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx @c15t/cli@alpha codemods use-consent-manager-to-hooks --dry-run --json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Review the proposed files, then run it again without `--dry-run`. For this v2
|
|
134
|
+
component:
|
|
135
|
+
|
|
136
|
+
```tsx title="Before (v2): components/accept-button.tsx"
|
|
137
|
+
import { useConsentManager } from '@c15t/react';
|
|
138
|
+
|
|
139
|
+
export function AcceptButton() {
|
|
140
|
+
const { activeUI, has, saveConsents } = useConsentManager();
|
|
141
|
+
if (activeUI !== 'banner' || has('marketing')) return null;
|
|
142
|
+
return (
|
|
143
|
+
<button type="button" onClick={() => void saveConsents('all')}>
|
|
144
|
+
Accept
|
|
145
|
+
</button>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
the codemod writes:
|
|
151
|
+
|
|
152
|
+
```tsx title="After (v3): components/accept-button.tsx"
|
|
153
|
+
import { useActiveUI, useConsent } from '@c15t/react';
|
|
154
|
+
import { useHeadlessConsentUI } from '@c15t/react/headless';
|
|
155
|
+
|
|
156
|
+
export function AcceptButton() {
|
|
157
|
+
const activeUI = useActiveUI() ?? 'none';
|
|
158
|
+
const hasMarketing = useConsent('marketing');
|
|
159
|
+
const { saveCustomPreferences: saveConsents } = useHeadlessConsentUI();
|
|
160
|
+
if (activeUI !== 'banner' || hasMarketing) return null;
|
|
161
|
+
return (
|
|
162
|
+
<button type="button" onClick={() => void saveConsents('all')}>
|
|
163
|
+
Accept
|
|
164
|
+
</button>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The codemod keeps the entry you imported from. Change `@c15t/react` to
|
|
170
|
+
`c15t/react` afterwards if you moved to the umbrella package. Fields it cannot
|
|
171
|
+
rewrite stay on a `useConsentManager()` call under a `TODO(c15t v3)` comment,
|
|
172
|
+
which fails the build until you replace them. The table lists the v2 fields
|
|
173
|
+
components read. The other fields held provider configuration, such as
|
|
174
|
+
`legalLinks`, `scripts` and `storageConfig`, or store internals; set
|
|
175
|
+
configuration through `ConsentProvider` props.
|
|
176
|
+
Import hooks from `c15t/react`, `c15t/next` or `c15t/tanstack-start`, and
|
|
177
|
+
`useHeadlessConsentUI` from the matching `/headless` entry.
|
|
178
|
+
|
|
179
|
+
| v2 field | v3 replacement |
|
|
180
|
+
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
181
|
+
| `activeUI` | `useActiveUI()`. It returns `null` until c15t picks a surface, where v2 returned `'none'`. |
|
|
182
|
+
| `setActiveUI(ui)` | `useSetActiveUI()` |
|
|
183
|
+
| `has(category)` | `useConsent(category)`, one call per category at the top of the component |
|
|
184
|
+
| `consents` | `useConsents()` |
|
|
185
|
+
| `saveConsents('all')` | `useHeadlessConsentUI().saveCustomPreferences('all')` |
|
|
186
|
+
| `saveConsents('necessary')` | `useHeadlessConsentUI().saveCustomPreferences('none')` |
|
|
187
|
+
| `saveConsents('custom')` | `useConsentDraft().save()`, or `useHeadlessConsentUI().saveCustomPreferences()` inside a `ConsentDraftProvider` |
|
|
188
|
+
| `selectedConsents` | `useConsentDraft().values` |
|
|
189
|
+
| `setSelectedConsent(name, value)` | `useConsentDraft().set(name, value)` |
|
|
190
|
+
| `setConsent(name, value)` | `useSaveConsents()`, called with `{ [name]: value }` |
|
|
191
|
+
| `consentInfo` | `useExplicitChoice()` |
|
|
192
|
+
| `hasConsented()` | `useExplicitChoice() !== null` |
|
|
193
|
+
| `consentCategories`, `consentTypes`, `getDisplayedConsents()` | `useConsentDraft().displayedCategories`, with labels from `useTranslations().consentTypes` |
|
|
194
|
+
| `policyCategories` | `usePolicyCategories()`. The v2 list started with `'necessary'`; this one does not. |
|
|
195
|
+
| `policyScopeMode` | `usePolicyScopeMode()` |
|
|
196
|
+
| `policyBanner` | `usePromptPresentation()` |
|
|
197
|
+
| `policyDialog` | `usePreferencesPresentation()` |
|
|
198
|
+
| `model` | `useModel()`. It returns `null` while no policy matches, where v2 returned `'opt-in'`. |
|
|
199
|
+
| `branding` | `useBranding()`. It returns `null` when unset, where v2 returned `'c15t'`. |
|
|
200
|
+
| `iab` | `useIABSnapshot()` |
|
|
201
|
+
| `locationInfo` | `useLocation()` |
|
|
202
|
+
| `overrides`, `setOverrides()` | `useOverrides()`, `useSetOverrides()` |
|
|
203
|
+
| `setLanguage()` | `useSetLanguage()` |
|
|
204
|
+
| `user`, `identifyUser()` | `useUser()`, `useIdentify()` |
|
|
205
|
+
| `translationConfig` | `useTranslations()` for the active strings |
|
|
206
|
+
| `subscribeToConsentChanges(listener)` | `useSubscribeToConsentChanges()`, or the `onPermissionsChanged` callback |
|
|
207
|
+
| `updateConsentCategories(categories)` | `useRegisterConsentCategories()` |
|
|
208
|
+
| `manager` | Nothing. Use the hooks above. |
|
|
209
|
+
|
|
210
|
+
`saveCustomPreferences()` saves the way the stock buttons do. It closes the
|
|
211
|
+
banner or dialog after a successful save. `useSaveConsents()` calls the engine
|
|
212
|
+
directly and does not close anything.
|
|
213
|
+
|
|
214
|
+
In v2, `useConsentManager()` kept one draft per component. In v3,
|
|
215
|
+
`useConsentDraft()` and `useHeadlessConsentUI()` share a draft only inside a
|
|
216
|
+
`ConsentDraftProvider`. Without one, `saveCustomPreferences()` saves a fresh
|
|
217
|
+
draft and ignores what `useConsentDraft().set()` staged, even in the same
|
|
218
|
+
component. Save with `useConsentDraft().save()`, or render the code that stages
|
|
219
|
+
and the code that saves inside one `ConsentDraftProvider`. `ConsentWidget`
|
|
220
|
+
already includes one. The codemod
|
|
221
|
+
adds a `TODO(c15t v3)` comment where this applies.
|
|
222
|
+
|
|
223
|
+
## Replace `ConsentManagerProvider`
|
|
224
|
+
|
|
225
|
+
Swap `ConsentManagerProvider` for `ConsentProvider` from `c15t/react`, and pass
|
|
226
|
+
a transport as `mode`:
|
|
227
|
+
|
|
228
|
+
| v2 option | v3 option |
|
|
229
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
230
|
+
| `mode: 'hosted'` with `backendURL` | `mode: hosted({ url })` |
|
|
231
|
+
| `mode: 'offline'` with `offlinePolicy` | `mode: offline({ policyRules })` |
|
|
232
|
+
| `mode: 'custom'` with `endpointHandlers` | `mode: custom(transport)`, where `transport` implements the v3 transport interface. `endpointHandlers` throws. |
|
|
233
|
+
|
|
234
|
+
```tsx title="After (v3): the provider, changed lines"
|
|
235
|
+
import { ConsentBanner, ConsentDialog, ConsentProvider, hosted } from 'c15t/react';
|
|
236
|
+
|
|
237
|
+
// Replace ConsentManagerProvider:
|
|
238
|
+
<ConsentProvider
|
|
239
|
+
options={{ mode: hosted({ url: 'https://your-project.c15t.dev' }) }}
|
|
240
|
+
>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The `consent-provider-options` codemod rewrites the hosted and offline modes
|
|
244
|
+
and adds the transport import. It can't translate `endpointHandlers`, so it
|
|
245
|
+
leaves a `TODO(c15t v3)` comment on a custom mode:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
npx @c15t/cli@alpha codemods consent-provider-options --dry-run --json
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Keep your backend URL. An existing hosted URL such as
|
|
252
|
+
`https://your-project.c15t.dev` keeps working with v3 clients. New Inth
|
|
253
|
+
projects use URLs such as `https://your-project.inth.app`. The
|
|
254
|
+
[React quickstart](./quickstart.md) shows the full provider,
|
|
255
|
+
and [data fetching](../../concepts/data-fetching.md) covers custom transports.
|
|
256
|
+
|
|
257
|
+
Offline mode keeps choices in the browser and records nothing on a server. Not
|
|
258
|
+
recommended for production environments.
|
|
259
|
+
|
|
260
|
+
If your app renders on the server with React Router or Remix, the banner still
|
|
261
|
+
mounts after hydration, as in v2. `fetchSSRData()` from `c15t/react/server`
|
|
262
|
+
can save the browser its first `/init` request. See
|
|
263
|
+
[rendering](./rendering.md).
|
|
264
|
+
|
|
265
|
+
## Replace callbacks
|
|
266
|
+
|
|
267
|
+
Callbacks stay under `callbacks` in your options, with new names:
|
|
268
|
+
|
|
269
|
+
| v2 callback | v3 callback |
|
|
270
|
+
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
271
|
+
| `onConsentSet` | `onPermissionsChanged` to react to what may run now. `onChoiceRecorded` to react to the visitor's accept, reject or save. |
|
|
272
|
+
| `onConsentChanged` | `onChoiceRecorded`. See the note below the table. |
|
|
273
|
+
| `onBannerFetched` | No callback. In React and Next.js, read `usePolicyResolution()` to know when the policy has resolved. In JavaScript, read `resolution` from the kernel snapshot. |
|
|
274
|
+
| `onError`, `onBeforeConsentRevocationReload` | Unchanged |
|
|
275
|
+
|
|
276
|
+
The `callbacks-to-v3` codemod renames `onConsentChanged` to
|
|
277
|
+
`onChoiceRecorded`. It leaves a `TODO(c15t v3)` comment there about the new
|
|
278
|
+
payload, and on `onConsentSet` and `onBannerFetched`, which it can't choose a
|
|
279
|
+
replacement for:
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
npx @c15t/cli@alpha codemods callbacks-to-v3 --dry-run --json
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
v2's `onConsentChanged` fired only when a save changed at least one category's
|
|
286
|
+
value, and passed `preferences` and `previousPreferences`. v3's
|
|
287
|
+
`onChoiceRecorded` fires for every accept, reject or save that confirms a
|
|
288
|
+
category, including one that saves the same values again, because each save
|
|
289
|
+
renews the choice's confirmation time. Its payload carries `snapshot`,
|
|
290
|
+
`confirmed` and `actionAt`. v3 has no callback that fires only when a save
|
|
291
|
+
changes a value. If you need the before and after values, use
|
|
292
|
+
`onPermissionsChanged`: it passes `previous` and the new `snapshot` whenever
|
|
293
|
+
effective permissions change, whether a save or something else changed them.
|
|
294
|
+
|
|
295
|
+
v2's `onConsentSet` also fired on initialization and hydration. In v3,
|
|
296
|
+
`onChoiceRecorded` runs only when the visitor acts, and `onPermissionsChanged`
|
|
297
|
+
runs when effective permissions change for any reason, including expiry, a
|
|
298
|
+
policy update or a privacy signal. Neither runs at startup if the resolved
|
|
299
|
+
permissions match the defaults, such as an opt-in policy that leaves every
|
|
300
|
+
category denied. If an external API needs the starting state, read it once at
|
|
301
|
+
startup as well: in React and Next.js from `useEffectivePermissions()` in an
|
|
302
|
+
effect, which also runs on later changes, and in JavaScript from
|
|
303
|
+
`kernel.events.on('init:applied', ({ snapshot }) => ...)`.
|
|
304
|
+
|
|
305
|
+
```ts title="After (v3): callbacks in your options"
|
|
306
|
+
callbacks: {
|
|
307
|
+
onChoiceRecorded(event) {
|
|
308
|
+
console.log('Visitor recorded a choice', event);
|
|
309
|
+
},
|
|
310
|
+
onPermissionsChanged(event) {
|
|
311
|
+
console.log('Effective permissions changed', event);
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Move policy packs to policy rules
|
|
317
|
+
|
|
318
|
+
v2 policy packs become policy rules, and `policyPackPresets` becomes
|
|
319
|
+
`policyRulePresets`, exported from `c15t`. Where the rules live depends on your
|
|
320
|
+
backend:
|
|
321
|
+
|
|
322
|
+
* **Inth**: set the rules in your Inth project. Rules in client code do not
|
|
323
|
+
change a hosted policy.
|
|
324
|
+
* **Self-hosted**: move the backend's `policyPacks` to `manifest.policyRules`.
|
|
325
|
+
See [policy configuration](https://c15t.com/docs/self-host/guides/policy-packs).
|
|
326
|
+
* **Offline**: move `offlinePolicy.policyPacks` to `offline({ policyRules })`.
|
|
327
|
+
|
|
328
|
+
```tsx title="After (v3): offline policy rules"
|
|
329
|
+
import { offline, policyRulePresets } from 'c15t';
|
|
330
|
+
|
|
331
|
+
const mode = offline({
|
|
332
|
+
policyRules: [
|
|
333
|
+
policyRulePresets.europeOptIn(),
|
|
334
|
+
policyRulePresets.worldOptOutNoPrompt(),
|
|
335
|
+
],
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
| v2 preset | v3 preset |
|
|
340
|
+
| ----------------------------------------- | ----------------------- |
|
|
341
|
+
| `europeOptIn()`, `europeIab()` | Same names |
|
|
342
|
+
| `californiaOptIn()`, `californiaOptOut()` | Same names |
|
|
343
|
+
| `quebecOptIn()` | Same name |
|
|
344
|
+
| `worldNoBanner()` | `worldOptOutNoPrompt()` |
|
|
345
|
+
|
|
346
|
+
`worldNone()` is a separate, new preset: it shows nothing and grants no
|
|
347
|
+
rights, while `worldOptOutNoPrompt()` keeps v2's opt-out and preference rights.
|
|
348
|
+
|
|
349
|
+
The `policy-packs-to-policy-rules` codemod renames `policyPackPresets` and
|
|
350
|
+
`worldNoBanner()`, and moves the import to `c15t` where it came from
|
|
351
|
+
`c15t/react` or `c15t/next`. `consent-provider-options` moves
|
|
352
|
+
`offlinePolicy.policyPacks` into `offline({ policyRules })`. Preset calls need
|
|
353
|
+
no other change, but a hand-written v2 pack uses `consent` and `ui` keys,
|
|
354
|
+
which v3 rules replace with flat keys such as `model` and `prompt`; the
|
|
355
|
+
codemods mark those with a `TODO(c15t v3)` comment.
|
|
356
|
+
|
|
357
|
+
v3 adds presets for more regions. `offline()` without `policyRules` uses
|
|
358
|
+
`recommendedPolicyRules()`. Read [policies](../../concepts/policies.md) before you
|
|
359
|
+
change a rule's model or prompt, and
|
|
360
|
+
[how consent works](../../concepts/how-consent-works.md) for the difference
|
|
361
|
+
between a permission and a recorded choice.
|
|
362
|
+
|
|
363
|
+
## Rename options and components
|
|
364
|
+
|
|
365
|
+
| v2 | v3 |
|
|
366
|
+
| -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
367
|
+
| `translations` | `i18n` |
|
|
368
|
+
| `backendURL`, `mode: 'hosted'` | `mode: hosted({ url })` |
|
|
369
|
+
| `ssrData` (Next.js) | `ConsentRoot` with `state` from `resolveConsent()` |
|
|
370
|
+
| `iframeBlockerConfig` | `iframeBlocker`, or `false` to turn it off |
|
|
371
|
+
| `Frame` | `ConsentGate`. `Frame` still works as a deprecated alias. |
|
|
372
|
+
| `frame` translations, such as `frame.title` | `consentGate`, such as `consentGate.title`. Copy under `frame` still applies and logs a warning outside production. |
|
|
373
|
+
| `FrameTranslations` | `ConsentGateTranslations`. `FrameTranslations` still works as a deprecated alias. |
|
|
374
|
+
| `--frame-*` CSS variables | `--consent-gate-*`, with the same suffixes, such as `--consent-gate-placeholder-background-color`. The old names no longer apply. |
|
|
375
|
+
| `YouTubeEmbed`, `GoogleMap` | Removed. Wrap your own embed in `ConsentGate`. |
|
|
376
|
+
| `useConsentScript()` | Removed. Register the script in `scripts` with a `@c15t/integrations` helper. |
|
|
377
|
+
| `useSSRStatus()` | Removed |
|
|
378
|
+
| `ConsentButton` | Removed. Use the `ConsentBanner` and `ConsentWidget` buttons, or call `useHeadlessConsentUI()` from your own button. |
|
|
379
|
+
| `@c15t/react/cookie-banner` | `c15t/react/consent-banner` |
|
|
380
|
+
| `retryConfig` | Removed |
|
|
381
|
+
| `store`, including `namespace`, `debug`, `initialConsentCategories` and `initialTranslationConfig` | Removed. Use the top-level `consentCategories` and `i18n` options. Debug information is on `window.c15t`. |
|
|
382
|
+
| `window.c15tStore` | `window.c15t` |
|
|
383
|
+
| `iab: iab({ ... })` | `IABProvider`. See [Move IAB setup](#move-iab-setup-to-iabprovider). |
|
|
384
|
+
|
|
385
|
+
The `consent-provider-options` codemod renames `ConsentManagerProvider` and
|
|
386
|
+
its option and prop types, turns `mode` and `backendURL` into `hosted()` or
|
|
387
|
+
`offline()`, and renames `iframeBlockerConfig`. v2 still accepted the
|
|
388
|
+
deprecated `translations` option; the `translations-to-i18n` codemod moves it
|
|
389
|
+
to `i18n`.
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
npx @c15t/cli@alpha codemods consent-provider-options translations-to-i18n --dry-run --json
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
`ConsentBanner`, `ConsentDialog`, `ConsentDialogLink`, `ConsentDialogTrigger`
|
|
396
|
+
and `ConsentWidget` keep their names. The `scripts`, `networkBlocker`,
|
|
397
|
+
`legalLinks`, `overrides`, `theme` and `colorScheme` options keep theirs too,
|
|
398
|
+
but `theme` tokens no longer produce CSS on their own. See
|
|
399
|
+
[Update styles and themes](#update-styles-and-themes).
|
|
400
|
+
|
|
401
|
+
`persistence` and `storageConfig` are read once, when the provider mounts. v2
|
|
402
|
+
moved a stored choice when the storage key changed. Remount the provider to
|
|
403
|
+
change storage. Reading a vendor that nothing declares, through `vendors`, a
|
|
404
|
+
script or the backend, returns `false`.
|
|
405
|
+
|
|
406
|
+
Some names left the root `c15t/react` entry:
|
|
407
|
+
|
|
408
|
+
| v2 import from the root | v3 import |
|
|
409
|
+
| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
410
|
+
| `useHeadlessConsentUI`, `useConsentDialogTrigger`, `useColorScheme`, `useFocusTrap` and the translation helpers | `c15t/react/headless` |
|
|
411
|
+
| Flat banner parts such as `ConsentBannerCard` and `ConsentBannerAcceptButton` | `ConsentBanner.Card`, `ConsentBanner.AcceptButton` and the other compound parts, or the same names from `c15t/react/components/consent-banner` |
|
|
412
|
+
| `TriggerRoot`, `TriggerButton`, `TriggerIcon`, `TriggerText`, `useTriggerContext`, `useDraggable` | `c15t/react/consent-dialog-trigger` |
|
|
413
|
+
| Token types such as `ColorTokens` and `TypographyTokens` | `c15t/react/types`. `Theme` stays on the root. |
|
|
414
|
+
| `configureConsentManager`, `policyPackPresets`, `ConsentStoreState`, `ConsentManagerInterface` | Removed. Use `policyRulePresets` from `c15t`. |
|
|
415
|
+
|
|
416
|
+
The `root-exports-to-subpaths` codemod moves these imports to their new
|
|
417
|
+
entries and marks removed names with a `TODO(c15t v3)` comment:
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
npx @c15t/cli@alpha codemods root-exports-to-subpaths --dry-run --json
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
`ConsentGate` changes one behavior from `Frame`. When its category is granted,
|
|
424
|
+
the embed now mounts after hydration instead of in the server HTML, so an
|
|
425
|
+
iframe loads once instead of twice. Size the wrapper with `className` or
|
|
426
|
+
`style` to hold its space. Test ids change from `frame-placeholder` and
|
|
427
|
+
`frame-open-dialog` to `consent-gate-placeholder` and `consent-gate-button`.
|
|
428
|
+
|
|
429
|
+
## Update styles and themes
|
|
430
|
+
|
|
431
|
+
### Render theme CSS
|
|
432
|
+
|
|
433
|
+
In v2 the provider built CSS from `options.theme` in the browser. In v3 it
|
|
434
|
+
doesn't. Without a change, custom colors, fonts and radii fall back to the
|
|
435
|
+
defaults, and you only see a development warning.
|
|
436
|
+
|
|
437
|
+
Render `ConsentTheme` with the same theme object, or put the output of
|
|
438
|
+
`generateThemeCSS(theme)` in your own stylesheet. Keep `options.theme` for
|
|
439
|
+
`consentActions` and `slots`. See
|
|
440
|
+
[customization](../../customization/overview.md) and
|
|
441
|
+
[tokens](../../customization/tokens.md).
|
|
442
|
+
|
|
443
|
+
The `theme-to-consent-theme` codemod marks a provider `theme` that sets
|
|
444
|
+
tokens with a `TODO(c15t v3)` comment.
|
|
445
|
+
|
|
446
|
+
A generated theme uses `:root:root`, so your own `--c15t-*` values on plain
|
|
447
|
+
`:root` lose to it. Put them in the theme instead, or raise your selector.
|
|
448
|
+
|
|
449
|
+
### Stylesheets
|
|
450
|
+
|
|
451
|
+
* Dialog and preference styles moved from `styles.css` to
|
|
452
|
+
`@c15t/ui/styles/dialog.css`. The React, Next.js and TanStack Start dialogs
|
|
453
|
+
import it themselves. Import it yourself only if you render `@c15t/ui`
|
|
454
|
+
class maps for those parts.
|
|
455
|
+
* `iab/styles.css` no longer repeats the default tokens. Import it after
|
|
456
|
+
`styles.css`.
|
|
457
|
+
* Tailwind CSS 3 builds now fail without a PostCSS plugin, even with
|
|
458
|
+
`styles.tw3.css`, because the dialog stylesheet is imported from JavaScript.
|
|
459
|
+
Add `'c15t/postcss-tailwind3': {}` before `tailwindcss` in
|
|
460
|
+
`postcss.config`. See [Tailwind CSS 3](../../customization/tailwind.md#set-up-tailwind-css-3).
|
|
461
|
+
Create React App can't run the plugin. The `postcss-tailwind3` codemod adds
|
|
462
|
+
it to an object-form `postcss.config` and warns about configs it can't
|
|
463
|
+
edit.
|
|
464
|
+
|
|
465
|
+
### CSS variables and slots
|
|
466
|
+
|
|
467
|
+
| v2 | v3 |
|
|
468
|
+
| --------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
469
|
+
| `--consent-widget-*` | `--consent-manager-*`, used by `ConsentDialog` and `ConsentWidget` |
|
|
470
|
+
| `--consent-widget-accordion-*` | Removed. Set `--accordion-*` instead. |
|
|
471
|
+
| `--frame-*` | `--consent-gate-*` |
|
|
472
|
+
| `--consent-banner-entry-animation`, `--consent-banner-exit-animation` | Removed |
|
|
473
|
+
| `theme.slots.frame` | `consentGate`, `consentGateTitle` and `consentGateButton` |
|
|
474
|
+
| `theme.slots.consentDialogFooter` | `consentWidgetFooter` |
|
|
475
|
+
|
|
476
|
+
The `css-variables-to-v3` codemod renames `--consent-widget-*` and
|
|
477
|
+
`--frame-*` in stylesheets and inline styles, and marks
|
|
478
|
+
`--consent-widget-accordion-*` with a comment instead of renaming it, because
|
|
479
|
+
`--accordion-*` styles every accordion. `theme-to-consent-theme` renames
|
|
480
|
+
`theme.slots.consentDialogFooter` and marks `theme.slots.frame`.
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
npx @c15t/cli@alpha codemods theme-to-consent-theme css-variables-to-v3 postcss-tailwind3 --dry-run --json
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
`theme.slots` now applies in React. v2 accepted it in types and ignored it, so
|
|
487
|
+
check slots you set before the upgrade.
|
|
488
|
+
|
|
489
|
+
## Move IAB setup to `IABProvider`
|
|
490
|
+
|
|
491
|
+
React, Next.js and TanStack Start no longer take an `iab` provider option.
|
|
492
|
+
Render `IABProvider` from `c15t/react/iab` inside `ConsentProvider` or
|
|
493
|
+
`ConsentRoot`, around the IAB components. Its props are the options you passed
|
|
494
|
+
to `iab()`, such as `cmpId`, `vendors`, `customVendors` and the new
|
|
495
|
+
`publisherRestrictions`.
|
|
496
|
+
|
|
497
|
+
The `iab-option-to-iab-provider` codemod marks the `iab` option with a
|
|
498
|
+
`TODO(c15t v3)` comment; move it by hand.
|
|
499
|
+
|
|
500
|
+
A `cmpId` that isn't an integer from 2 to 4095 now throws. Without a `cmpId`,
|
|
501
|
+
the CMP doesn't mount, so there is no `__tcfapi` and no TC string.
|
|
502
|
+
|
|
503
|
+
`@c15t/iab` no longer exports `createIABManager`, `decodeTCString`,
|
|
504
|
+
`generateTCString` or `c15tToIabPurposes`. Use `createIAB({ kernel, cmpId })`,
|
|
505
|
+
which returns a handle with `generateTCString()`, `acceptAll()`, `rejectAll()`
|
|
506
|
+
and `save()`. If your app depends on `@c15t/iab` directly, upgrade it to
|
|
507
|
+
`@c15t/iab@alpha` too, or your import resolves to the v2 package. Remove it if
|
|
508
|
+
`IABProvider` now covers everything you used it for.
|
|
509
|
+
|
|
510
|
+
Server integrations now send a reference to the Global Vendor List instead of
|
|
511
|
+
the list, and the browser fetches it when the IAB module mounts. If you set a
|
|
512
|
+
Content Security Policy, allow the list's origin in `connect-src`, or `'self'`
|
|
513
|
+
for same-origin routes.
|
|
514
|
+
|
|
515
|
+
See the [IAB guide](./iab.md) for a complete setup.
|
|
516
|
+
|
|
517
|
+
## Update headless UI code
|
|
518
|
+
|
|
519
|
+
`useHeadlessConsentUI()` keeps its name and moves to `c15t/react/headless`.
|
|
520
|
+
The `root-exports-to-subpaths` codemod moves the import.
|
|
521
|
+
Some of its methods take different arguments:
|
|
522
|
+
|
|
523
|
+
| v2 | v3 |
|
|
524
|
+
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
525
|
+
| `performAction(action, { surface })` | `performAction(action)`. It also accepts `'dismiss'` and `'customize'`. |
|
|
526
|
+
| `openBanner({ force })` | `openBanner()` |
|
|
527
|
+
| `saveCustomPreferences({ uiSource })` | `saveCustomPreferences()`, `saveCustomPreferences('all')` or `saveCustomPreferences('none')` |
|
|
528
|
+
| `banner.layout`, `dialog.layout`, `hasPolicyHints` | `banner` and `dialog` carry `variant`, `position`, `rights`, `requiredActions` and `diagnostics` |
|
|
529
|
+
|
|
530
|
+
The `UseHeadlessConsentUIResult` type is no longer exported. Use
|
|
531
|
+
`ReturnType<typeof useHeadlessConsentUI>` if you need it.
|
|
532
|
+
|
|
533
|
+
## Update complete translation objects
|
|
534
|
+
|
|
535
|
+
Partial overrides through `i18n.messages` and objects typed as `Translations`
|
|
536
|
+
need no change. A complete translation object typed as `CompleteTranslations`
|
|
537
|
+
fails type-checking until you add the new sections and keys: `consentGate`, which
|
|
538
|
+
replaces `frame`, `rights`, the notice and dismiss strings such as
|
|
539
|
+
`common.acknowledge` and `cookieBanner.noticeTitle`, and the vendor list copy
|
|
540
|
+
under `consentManagerDialog.vendors`. `migrateLegacyTranslationKeys()` moves
|
|
541
|
+
`frame` copy to `consentGate`.
|
|
542
|
+
|
|
543
|
+
`i18n.messages` for the visitor's language now merge over the backend's copy
|
|
544
|
+
instead of being replaced by it.
|
|
545
|
+
|
|
546
|
+
## Keep visitors' existing choices
|
|
547
|
+
|
|
548
|
+
v3 uses the same `c15t` cookie and storage key as v2 and reads v2 records. It
|
|
549
|
+
does not rewrite them on page load; the next accept, reject or save writes a
|
|
550
|
+
v3 record.
|
|
551
|
+
|
|
552
|
+
A v2 denial keeps restricting its category. A v2 grant keeps applying until
|
|
553
|
+
the policy's validity period runs out, and then c15t asks again. If the rule
|
|
554
|
+
comes from a preset and the v2 record noted the policy it was made under, c15t
|
|
555
|
+
also asks again when that policy has changed. Under custom rules, a v2 grant
|
|
556
|
+
applies until it expires. Do not copy permissions into new
|
|
557
|
+
records yourself; c15t decides which v2 choices still count.
|
|
558
|
+
|
|
559
|
+
## Update dev tools
|
|
560
|
+
|
|
561
|
+
`@c15t/dev-tools/react` and `@c15t/dev-tools/tanstack` are gone. Import
|
|
562
|
+
`DevTools`, `C15tTanStackDevtoolsPanel` and `c15tDevtools()` from
|
|
563
|
+
`c15t/react/devtools`. Dev tools read the consent engine, so `namespace`,
|
|
564
|
+
`createStoreConnector()` and `getC15tStore()` are gone too. See
|
|
565
|
+
[dev tools](./components/dev-tools.md).
|
|
566
|
+
|
|
567
|
+
The `dev-tools-to-c15t` codemod points the imports at `c15t/react/devtools` and
|
|
568
|
+
removes the `namespace` prop:
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
npx @c15t/cli@alpha codemods dev-tools-to-c15t --dry-run --json
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
## Update the CLI
|
|
575
|
+
|
|
576
|
+
* Run `c15t login` again. The CLI signs in through Inth now and no longer
|
|
577
|
+
reads sessions saved by v2.
|
|
578
|
+
* `c15t setup` no longer writes `.env.local` or `.env.example` and no longer
|
|
579
|
+
accepts `--env`. It writes the backend URL into the files it generates.
|
|
580
|
+
* `c15t setup` no longer detects Remix or Gatsby. Pick a guide from
|
|
581
|
+
[Choose your setup](../../concepts/choose-your-setup.md) for those apps.
|
|
582
|
+
|
|
583
|
+
## Check the migration
|
|
584
|
+
|
|
585
|
+
1. Run your typecheck and build. Remaining `TODO(c15t v3)` comments from the
|
|
586
|
+
codemod fail the build until you resolve them.
|
|
587
|
+
2. Load the app with a v2 consent cookie from before the upgrade. The banner
|
|
588
|
+
stays closed if that choice still applies, and a rejected category stays
|
|
589
|
+
blocked.
|
|
590
|
+
3. In a private window, check in DevTools Network that vendor requests wait for
|
|
591
|
+
consent, start after you accept, and stay absent after you reject and
|
|
592
|
+
reload.
|
|
593
|
+
4. Reopen preferences from your privacy settings link and change a category.
|
|
594
|
+
|
|
595
|
+
The [verification guide](../../guides/verify-consent.md) has the full release
|
|
596
|
+
checklist.
|
|
@@ -66,6 +66,12 @@ helper sets `vendor` to its script ID, so `xPixel()` is `x-pixel`, `gtag()`
|
|
|
66
66
|
is `gtag` and `cloudflareZaraz()` is `cloudflare-zaraz`. Each
|
|
67
67
|
[vendor guide](../../integrations/overview.md) names its slug.
|
|
68
68
|
|
|
69
|
+
Declaring a helper's vendor is optional. Without a declaration the script
|
|
70
|
+
still loads with its category, but the dialog has no switch for it. In
|
|
71
|
+
development, c15t logs a console warning that names the slug, so you can
|
|
72
|
+
declare it if visitors should be able to turn it off. Production builds
|
|
73
|
+
skip the warning.
|
|
74
|
+
|
|
69
75
|
A self-hosted backend can declare vendors too. `/init` returns them and c15t
|
|
70
76
|
merges them with the vendors in code. When both declare the same `id`, the
|
|
71
77
|
code declaration wins.
|