@c15t/react 3.1.0 → 3.2.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 +35 -5
- package/README.md +2 -2
- package/dist/component-hooks/use-consent-gate.js +2 -0
- package/dist/component-hooks/use-consent-labels.js +2 -0
- package/dist/component-hooks/use-consent-text-direction.js +2 -0
- package/dist/component-hooks/use-vendors-listed-under.js +2 -0
- package/dist/components/consent-gate/atoms.js +1 -1
- package/dist/components/consent-gate/consent-gate.js +1 -1
- package/dist/components/iab-prompt/atoms/root.js +1 -1
- package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
- package/dist/components/preferences/atoms/button.js +1 -1
- package/dist/components/preferences/atoms/vendor-list.js +1 -1
- package/dist/components/prompt/atoms/root.js +1 -1
- package/dist/components/prompt/components.js +1 -1
- package/dist/components/prompt/policy-actions.js +1 -1
- package/dist/components/prompt/prompt.js +1 -1
- package/dist/components/prompt/surface-context.js +1 -1
- package/dist/components/prompt/use-banner-copy.js +1 -1
- package/dist/components/shared/streamed-surface.js +1 -1
- package/dist/headless.js +1 -1
- package/dist/index-parts/offline.js +1 -1
- package/dist/modes.js +1 -1
- package/dist/styles.tw3.css +2 -2
- package/dist/transports/offline.js +1 -1
- package/dist/version.js +1 -1
- package/dist-types/component-hooks/use-consent-gate.d.ts +102 -0
- package/dist-types/component-hooks/use-consent-labels.d.ts +32 -0
- package/dist-types/component-hooks/use-consent-text-direction.d.ts +22 -0
- package/dist-types/component-hooks/use-vendors-listed-under.d.ts +30 -0
- package/dist-types/components/prompt/atoms/root.d.ts +3 -3
- package/dist-types/components/prompt/prompt.d.ts +4 -3
- package/dist-types/components/prompt/surface-context.d.ts +1 -14
- package/dist-types/components/prompt/use-banner-copy.d.ts +8 -3
- package/dist-types/components/shared/streamed-surface.d.ts +47 -10
- package/dist-types/headless.d.ts +10 -1
- package/dist-types/hooks/use-iab-policy-guard.d.ts +17 -1
- package/dist-types/hooks/use-scroll-lock.d.ts +16 -2
- package/dist-types/index-parts/offline.d.ts +1 -1
- package/dist-types/modes.d.ts +1 -1
- package/dist-types/module-hooks/script-loader.d.ts +9 -0
- package/dist-types/provider.d.ts +3 -3
- package/dist-types/transports/offline.d.ts +2 -2
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +35 -5
- package/docs/concepts/choose-your-setup.md +9 -9
- package/docs/concepts/consent-state.md +81 -9
- package/docs/concepts/data-fetching.md +30 -23
- package/docs/concepts/modes.md +87 -35
- package/docs/customization/recipes.md +4 -4
- package/docs/customization/stylesheets.md +42 -3
- package/docs/customization/tailwind.md +17 -34
- package/docs/customization/translations.md +18 -12
- package/docs/frameworks/react/clear-on-revocation.md +3 -2
- package/docs/frameworks/react/components/consent-banner.md +3 -3
- package/docs/frameworks/react/components/consent-provider.md +29 -29
- package/docs/frameworks/react/gpp.md +1 -1
- package/docs/frameworks/react/headless.md +146 -19
- package/docs/frameworks/react/hooks.md +2 -1
- package/docs/frameworks/react/iab.md +8 -1
- package/docs/frameworks/react/quickstart.md +2 -2
- package/docs/frameworks/react/rendering.md +1 -1
- package/docs/frameworks/react/translations.md +9 -9
- package/docs/frameworks/react/troubleshooting.md +1 -1
- package/docs/frameworks/react/upgrade-v3.md +39 -32
- package/docs/frameworks/react/vendor-consent.md +5 -0
- package/docs/guides/banner-experiments.md +3 -3
- package/docs/guides/troubleshooting.md +3 -2
- package/docs/integrations/activecampaign.md +297 -0
- package/docs/integrations/ada.md +396 -0
- package/docs/integrations/adobe-analytics.md +2 -2
- package/docs/integrations/adobe-target.md +460 -0
- package/docs/integrations/adroll.md +336 -0
- package/docs/integrations/ahrefs-analytics.md +2 -2
- package/docs/integrations/amazon-ads.md +424 -0
- package/docs/integrations/amplitude.md +2 -2
- package/docs/integrations/appcues.md +368 -0
- package/docs/integrations/attentive.md +313 -0
- package/docs/integrations/building-integrations.md +29 -16
- package/docs/integrations/clay.md +305 -0
- package/docs/integrations/clearbit.md +3 -3
- package/docs/integrations/cloudflare-web-analytics.md +2 -2
- package/docs/integrations/cloudflare-zaraz.md +2 -2
- package/docs/integrations/crisp.md +2 -2
- package/docs/integrations/customer-io.md +369 -0
- package/docs/integrations/databuddy.md +127 -52
- package/docs/integrations/default-com.md +497 -0
- package/docs/integrations/dynamic-yield.md +373 -0
- package/docs/integrations/fathom-analytics.md +2 -2
- package/docs/integrations/front-chat.md +2 -2
- package/docs/integrations/fullstory.md +298 -0
- package/docs/integrations/gainsight-px.md +418 -0
- package/docs/integrations/google-maps.md +2 -2
- package/docs/integrations/google-tag-manager.md +59 -2
- package/docs/integrations/google-tag.md +2 -2
- package/docs/integrations/gorgias.md +365 -0
- package/docs/integrations/heap.md +2 -2
- package/docs/integrations/hightouch.md +2 -2
- package/docs/integrations/hotjar.md +40 -26
- package/docs/integrations/hubspot.md +529 -0
- package/docs/integrations/insider.md +550 -0
- package/docs/integrations/intercom.md +2 -2
- package/docs/integrations/klaviyo.md +2 -2
- package/docs/integrations/linkedin-insights.md +2 -2
- package/docs/integrations/liveintent.md +327 -0
- package/docs/integrations/logrocket.md +2 -2
- package/docs/integrations/lucky-orange.md +336 -0
- package/docs/integrations/marketo.md +309 -0
- package/docs/integrations/matomo-analytics.md +2 -2
- package/docs/integrations/meta-pixel.md +2 -2
- package/docs/integrations/microsoft-clarity.md +2 -2
- package/docs/integrations/microsoft-uet.md +2 -2
- package/docs/integrations/mixpanel-analytics.md +2 -2
- package/docs/integrations/one-dollar-stats.md +2 -2
- package/docs/integrations/openai-pixel.md +2 -2
- package/docs/integrations/optimizely.md +411 -0
- package/docs/integrations/oracle-eloqua.md +301 -0
- package/docs/integrations/overview.md +128 -81
- package/docs/integrations/pendo.md +339 -0
- package/docs/integrations/pinterest-tag.md +2 -2
- package/docs/integrations/pirsch.md +2 -2
- package/docs/integrations/plausible-analytics.md +2 -2
- package/docs/integrations/posthog.md +2 -2
- package/docs/integrations/promptwatch.md +2 -2
- package/docs/integrations/rb2b.md +363 -0
- package/docs/integrations/reddit-pixel.md +2 -2
- package/docs/integrations/rudderstack.md +2 -2
- package/docs/integrations/rybbit-analytics.md +2 -2
- package/docs/integrations/salesforce-pardot.md +342 -0
- package/docs/integrations/segment.md +2 -2
- package/docs/integrations/sentry.md +2 -2
- package/docs/integrations/shopify.md +323 -0
- package/docs/integrations/snapchat-pixel.md +2 -2
- package/docs/integrations/statable-analytics.md +345 -0
- package/docs/integrations/the-trade-desk.md +328 -0
- package/docs/integrations/tiktok-pixel.md +2 -2
- package/docs/integrations/umami-analytics.md +2 -2
- package/docs/integrations/vector.md +333 -0
- package/docs/integrations/vercel-analytics.md +2 -2
- package/docs/integrations/vercel-speed-insights.md +7 -2
- package/docs/integrations/wisepops.md +416 -0
- package/docs/integrations/x-pixel.md +2 -2
- package/docs/integrations/youtube.md +2 -2
- package/docs/integrations/zendesk.md +359 -0
- package/package.json +10 -32
- package/dist/components/prompt/resolve-banner-primary-actions.js +0 -1
- package/dist-types/components/prompt/resolve-banner-primary-actions.d.ts +0 -14
|
@@ -68,35 +68,35 @@ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
|
|
|
68
68
|
`iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
|
|
69
69
|
`enabled` apply when they change.
|
|
70
70
|
|
|
71
|
-
| Option | Purpose
|
|
72
|
-
| ----------------------------- |
|
|
73
|
-
| `mode` | Required. `hosted()` for the backend's `/init`, `manifest()` for a bundled policy, `
|
|
74
|
-
| `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper
|
|
75
|
-
| `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md)
|
|
76
|
-
| `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md)
|
|
77
|
-
| `scriptLoader` | Options for the script loader module
|
|
78
|
-
| `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false`
|
|
79
|
-
| `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off
|
|
80
|
-
| `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default
|
|
81
|
-
| `storageConfig` | Storage key and cookie settings for the stored choice
|
|
82
|
-
| `clearOnRevocation` | Cookies and storage keys to delete when a category is denied
|
|
83
|
-
| `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true`
|
|
84
|
-
| `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope
|
|
85
|
-
| `overrides` | Force the country, region or language the policy is resolved for, for testing
|
|
86
|
-
| `user` | Link consent records to a signed-in user
|
|
87
|
-
| `callbacks` | Choice, permission and error events
|
|
88
|
-
| `i18n` | Languages and message overrides
|
|
89
|
-
| `legalLinks` | Privacy policy and other links shown in the banner and dialog
|
|
90
|
-
| `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md)
|
|
91
|
-
| `components` | Slot attributes per component part
|
|
92
|
-
| `presentation` | Banner and dialog shape, position and blocking
|
|
93
|
-
| `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md)
|
|
94
|
-
| `journey` | Random id that links each `/init` to the save that follows. See [session reports](../../../concepts/data-fetching.md#link-an-init-to-the-save-that-follows)
|
|
95
|
-
| `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone
|
|
96
|
-
| `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations
|
|
97
|
-
| `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior)
|
|
98
|
-
| `nonce` | Content Security Policy nonce for script elements c15t inserts
|
|
99
|
-
| `enabled` | Set `false` only when you deliberately bypass consent enforcement
|
|
71
|
+
| Option | Purpose |
|
|
72
|
+
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| `mode` | Required. `hosted()` for the backend's `/init`, `manifest()` for a bundled policy, `local()` for local policies, all from `c15t/react`, or `custom()` for your own transport. See [consent modes](../../../concepts/modes.md) |
|
|
74
|
+
| `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
|
|
75
|
+
| `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
|
|
76
|
+
| `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
|
|
77
|
+
| `scriptLoader` | Options for the script loader module |
|
|
78
|
+
| `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
|
|
79
|
+
| `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
|
|
80
|
+
| `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
|
|
81
|
+
| `storageConfig` | Storage key and cookie settings for the stored choice |
|
|
82
|
+
| `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
|
|
83
|
+
| `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
|
|
84
|
+
| `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
|
|
85
|
+
| `overrides` | Force the country, region or language the policy is resolved for, for testing |
|
|
86
|
+
| `user` | Link consent records to a signed-in user |
|
|
87
|
+
| `callbacks` | Choice, permission and error events |
|
|
88
|
+
| `i18n` | Languages and message overrides |
|
|
89
|
+
| `legalLinks` | Privacy policy and other links shown in the banner and dialog |
|
|
90
|
+
| `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
|
|
91
|
+
| `components` | Slot attributes per component part |
|
|
92
|
+
| `presentation` | Banner and dialog shape, position and blocking |
|
|
93
|
+
| `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
|
|
94
|
+
| `journey` | Random id that links each `/init` to the save that follows. See [session reports](../../../concepts/data-fetching.md#link-an-init-to-the-save-that-follows) |
|
|
95
|
+
| `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
|
|
96
|
+
| `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
|
|
97
|
+
| `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
|
|
98
|
+
| `nonce` | Content Security Policy nonce for script elements c15t inserts |
|
|
99
|
+
| `enabled` | Set `false` only when you deliberately bypass consent enforcement |
|
|
100
100
|
|
|
101
101
|
`enabled: false` grants categories and allows gated loading while suppressing
|
|
102
102
|
consent UI. It is not a way to fix failed initialization or hide a banner in
|
|
@@ -36,7 +36,7 @@ The props are the GPP options listed below, for example
|
|
|
36
36
|
pass to `ConsentProvider` as `runtime`.
|
|
37
37
|
|
|
38
38
|
The GPP string follows the policy and location your backend resolves for the
|
|
39
|
-
visitor, so a US visitor's state comes from the backend. With `
|
|
39
|
+
visitor, so a US visitor's state comes from the backend. With `local()`
|
|
40
40
|
mode, pass the state yourself, for example
|
|
41
41
|
`overrides: { country: 'US', region: 'CA' }`.
|
|
42
42
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Headless
|
|
3
|
-
description: Build a custom consent banner in React with the
|
|
4
|
-
hooks inside your ConsentProvider.
|
|
3
|
+
description: Build a custom consent banner or embed gate in React with the
|
|
4
|
+
c15t/react/headless hooks inside your ConsentProvider.
|
|
5
5
|
group: frameworks
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -30,29 +30,28 @@ The headless hooks read the runtime from the nearest `ConsentProvider` from
|
|
|
30
30
|
banner, and keep the dialog and persistent preferences control.
|
|
31
31
|
|
|
32
32
|
```tsx title="src/consent-banner.tsx"
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
dismiss: 'acknowledge',
|
|
39
|
-
reject: 'rejectAll',
|
|
40
|
-
save: 'save',
|
|
41
|
-
} as const;
|
|
33
|
+
import {
|
|
34
|
+
useBannerCopy,
|
|
35
|
+
useConsentLabels,
|
|
36
|
+
useHeadlessConsentUI,
|
|
37
|
+
} from 'c15t/react/headless';
|
|
42
38
|
|
|
43
39
|
export const Banner = () => {
|
|
44
40
|
const { banner, performAction, openDialog } = useHeadlessConsentUI();
|
|
45
|
-
const {
|
|
41
|
+
const { title, description } = useBannerCopy();
|
|
42
|
+
const labels = useConsentLabels();
|
|
46
43
|
|
|
47
44
|
if (!banner.isVisible) {
|
|
48
45
|
return null;
|
|
49
46
|
}
|
|
50
47
|
|
|
51
48
|
return (
|
|
52
|
-
<section aria-label=
|
|
49
|
+
<section aria-label={title}>
|
|
50
|
+
<h2>{title}</h2>
|
|
51
|
+
<p>{description}</p>
|
|
53
52
|
{banner.preferenceControls.map((right) => (
|
|
54
53
|
<button key={right} type="button" onClick={openDialog}>
|
|
55
|
-
{right
|
|
54
|
+
{labels.preferenceControls[right]}
|
|
56
55
|
</button>
|
|
57
56
|
))}
|
|
58
57
|
{banner.actionGroups.map((group) => (
|
|
@@ -64,7 +63,7 @@ export const Banner = () => {
|
|
|
64
63
|
data-primary={banner.primaryActions.includes(action) || undefined}
|
|
65
64
|
onClick={() => performAction(action)}
|
|
66
65
|
>
|
|
67
|
-
{
|
|
66
|
+
{labels.actions[action]}
|
|
68
67
|
</button>
|
|
69
68
|
))}
|
|
70
69
|
</div>
|
|
@@ -76,8 +75,10 @@ export const Banner = () => {
|
|
|
76
75
|
|
|
77
76
|
`banner.actionGroups` is the resolved layout: reject and accept share a group
|
|
78
77
|
at equal prominence, and the rest follow. `banner.orderedActions` is the same
|
|
79
|
-
list flattened.
|
|
80
|
-
|
|
78
|
+
list flattened. `banner.primaryActions` lists the actions to style as
|
|
79
|
+
primary. Under a notice the only action is `dismiss`, and
|
|
80
|
+
`banner.primaryActions` contains it. `banner.preferenceControls` recommends
|
|
81
|
+
additional buttons for opening
|
|
81
82
|
preferences. Under a notice it contains `opt-out`, which selects the
|
|
82
83
|
"Do not sell or share my data" label. The example renders that button and
|
|
83
84
|
an "OK" button. Both controls keep their own command: opening
|
|
@@ -86,6 +87,20 @@ preferences and dismissing the notice.
|
|
|
86
87
|
The list is a rendering helper. It does not establish that your UI implements
|
|
87
88
|
all policy rights. Provide disclosure and persistent preferences access.
|
|
88
89
|
|
|
90
|
+
`useBannerCopy()` returns the `title` and `description` for the active
|
|
91
|
+
prompt. A notice reads `cookieBanner.noticeTitle` and
|
|
92
|
+
`cookieBanner.noticeDescription`, then falls back to the choice copy in the
|
|
93
|
+
same language before English. `useConsentLabels()` returns
|
|
94
|
+
`actions`, a label for each action, and `preferenceControls`, a label for
|
|
95
|
+
each preference control. `dismiss` reads `common.acknowledge`, then
|
|
96
|
+
`common.dismiss`. Both hooks follow the active language and fill missing
|
|
97
|
+
keys from English, and they are the same strings the pre-built banner
|
|
98
|
+
renders.
|
|
99
|
+
|
|
100
|
+
The hooks wrap `resolvePromptCopy`, `resolveActionLabels`, and
|
|
101
|
+
`resolvePreferenceControlLabels`. Those take a translations object and
|
|
102
|
+
are exported from `c15t`, so code outside React can produce the same copy.
|
|
103
|
+
|
|
89
104
|
`performAction` saves all categories for `accept`, none for `reject`, the
|
|
90
105
|
current draft for `save`, records a dismissal for `dismiss`, and opens the
|
|
91
106
|
preference center for `customize`. `banner.diagnostics` reports when a host
|
|
@@ -94,5 +109,117 @@ prominence. Review each diagnostic when configuring custom presentation.
|
|
|
94
109
|
|
|
95
110
|
`banner.variant`, `banner.position`, and `banner.blocking` carry the resolved
|
|
96
111
|
shape from `presentation.prompt`, so a headless surface can follow the same
|
|
97
|
-
bar, widget, or wall choice the pre-built banner would make
|
|
98
|
-
focus and
|
|
112
|
+
bar, widget, or wall choice the pre-built banner would make. The pre-built
|
|
113
|
+
banner traps focus and locks page scroll while `blocking` is true. To do
|
|
114
|
+
the same, call `useFocusTrap(blocking, ref)` and `useScrollLock(blocking)`
|
|
115
|
+
from the headless entry, with `blocking` set to
|
|
116
|
+
`banner.isVisible && banner.blocking` and `ref` attached to your banner's
|
|
117
|
+
root element. Scroll locks are counted, so the page scrolls again once the
|
|
118
|
+
banner and any open dialog have both let go.
|
|
119
|
+
|
|
120
|
+
c15t can show consent copy in the visitor's browser language, which may run
|
|
121
|
+
right to left on a left-to-right page. `useConsentTextDirection()` returns
|
|
122
|
+
`'rtl'` or `'ltr'` for the language the copy is in. Set it as `dir` on your
|
|
123
|
+
banner and preference center roots, as the pre-built ones do.
|
|
124
|
+
|
|
125
|
+
The pre-built banner and dialog render nothing under the IAB model, and
|
|
126
|
+
the TCF surfaces inside `<IABProvider>` take over. A
|
|
127
|
+
custom surface that handles only opt-in and opt-out should do the same:
|
|
128
|
+
return `null` when `useModel()` is `'iab'`. Call `useIABPolicyGuard()` in
|
|
129
|
+
it as well. It throws `IABUnavailableError` while rendering when the
|
|
130
|
+
policy needs IAB and the app never loaded `<IABProvider>`, so the gap
|
|
131
|
+
shows up in your error boundary instead of as a missing banner.
|
|
132
|
+
|
|
133
|
+
## Build a custom consent gate
|
|
134
|
+
|
|
135
|
+
`useConsentGate(category)` tells a custom gate what to render for one
|
|
136
|
+
consent category, with translated copy and an action that opens the
|
|
137
|
+
preference center. Use it when the stock `ConsentGate` placeholder does not
|
|
138
|
+
fit your design, for example a design system card in front of a video or
|
|
139
|
+
map embed. `ConsentGate` runs on the same hook, so both gates follow the
|
|
140
|
+
same rules.
|
|
141
|
+
|
|
142
|
+
This gate renders its children once `marketing` is granted and the page has
|
|
143
|
+
hydrated, the policy message when the policy blocks `marketing`, and
|
|
144
|
+
otherwise a placeholder with a button that opens the preference center:
|
|
145
|
+
|
|
146
|
+
```tsx title="src/video-gate.tsx"
|
|
147
|
+
import { useConsentGate } from 'c15t/react/headless';
|
|
148
|
+
import type { ReactNode } from 'react';
|
|
149
|
+
|
|
150
|
+
export const VideoGate = ({ children }: { children: ReactNode }) => {
|
|
151
|
+
const { state, title, actionLabel, policyBlockedMessage, requestConsent } =
|
|
152
|
+
useConsentGate('marketing');
|
|
153
|
+
|
|
154
|
+
if (state === 'granted') {
|
|
155
|
+
return children;
|
|
156
|
+
}
|
|
157
|
+
if (state === 'pending') {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
if (state === 'policy-blocked') {
|
|
161
|
+
return <p>{policyBlockedMessage}</p>;
|
|
162
|
+
}
|
|
163
|
+
return (
|
|
164
|
+
<div>
|
|
165
|
+
<p>{title}</p>
|
|
166
|
+
<button type="button" onClick={requestConsent}>
|
|
167
|
+
{actionLabel}
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Render `VideoGate` inside your `ConsentProvider` around the embed, such as a
|
|
175
|
+
YouTube `iframe`.
|
|
176
|
+
|
|
177
|
+
| Field | Type | Description |
|
|
178
|
+
| ---------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
179
|
+
| `state` | `ConsentGateState` | `'granted'`, `'pending'`, `'denied'` or `'policy-blocked'`. |
|
|
180
|
+
| `categoryTitle` | `string` | `consentTypes.<category>.title` from your translations, or the category name when they have none. |
|
|
181
|
+
| `title` | `string` or `undefined` | `consentGate.title`, `Accept {category} consent to view this content.`, with `{category}` replaced by `categoryTitle`. |
|
|
182
|
+
| `actionLabel` | `string` or `undefined` | `consentGate.actionButton`, `Enable {category} consent`, with `{category}` replaced by `categoryTitle`. |
|
|
183
|
+
| `policyBlockedMessage` | `string` | `consentGate.policyBlocked`, or the English message when your translations have none. |
|
|
184
|
+
| `requestConsent` | `() => void` | Opens the preference center. |
|
|
185
|
+
|
|
186
|
+
The result type is exported as `UseConsentGateResult` and the state type as
|
|
187
|
+
`ConsentGateState`. Render from `state`:
|
|
188
|
+
|
|
189
|
+
* `'granted'`: the effective permission for the category is granted and
|
|
190
|
+
the page has hydrated. Render the content.
|
|
191
|
+
* `'pending'`: permission is granted but the page has not hydrated yet.
|
|
192
|
+
Render nothing.
|
|
193
|
+
* `'denied'`: permission is not granted. Render a placeholder with a
|
|
194
|
+
button that calls `requestConsent`.
|
|
195
|
+
* `'policy-blocked'`: the policy has `scopeMode: 'strict'` and its scope
|
|
196
|
+
leaves the category out, so the visitor cannot grant it. Render
|
|
197
|
+
`policyBlockedMessage` without a button. `necessary` is never blocked.
|
|
198
|
+
|
|
199
|
+
Permission is the value `useConsent(category)` returns. An opt-out policy
|
|
200
|
+
can grant it before the visitor chooses, and a privacy signal such as GPC
|
|
201
|
+
can withdraw a stored grant.
|
|
202
|
+
|
|
203
|
+
Granted content never appears in the server HTML. When the server snapshot
|
|
204
|
+
grants the category, `state` is `'pending'` in the server HTML and during
|
|
205
|
+
hydration, then turns `'granted'`, so an iframe inside a streamed
|
|
206
|
+
`Suspense` boundary loads once. Server rendering and hydration evaluate the
|
|
207
|
+
snapshot at its own evaluation time and never read the clock, so the gate
|
|
208
|
+
works in a statically prerendered page.
|
|
209
|
+
|
|
210
|
+
`requestConsent` does not grant the category, because the visitor still has
|
|
211
|
+
to save. The preference center opens on its usual view, not on the
|
|
212
|
+
category. Mount a `ConsentDialog` in the same provider so it has something
|
|
213
|
+
to open. The stock gate button starts loading the pre-built dialog's code
|
|
214
|
+
on hover, focus and idle time. `requestConsent` doesn't, so without a
|
|
215
|
+
banner or dialog trigger on the page, the first open waits for that
|
|
216
|
+
download.
|
|
217
|
+
|
|
218
|
+
When it mounts, the hook registers the category with the provider, as
|
|
219
|
+
`ConsentGate` does, so the preference center can list it. If you set
|
|
220
|
+
`options.consentCategories` on the provider, list the category there too,
|
|
221
|
+
so the preference center shows it before a gate mounts, including on pages
|
|
222
|
+
without one.
|
|
223
|
+
|
|
224
|
+
The hook renders no wrapper element. Reserve the embed's space in your own
|
|
225
|
+
markup so the page does not shift when the placeholder is replaced.
|
|
@@ -65,6 +65,7 @@ Use focused hooks for feature gates. For a complete custom prompt or preference
|
|
|
65
65
|
center, use `useHeadlessConsentUI()` for surface actions and `useConsentDraft()`
|
|
66
66
|
for staged choices, inside one `ConsentDraftProvider` so the save commits the
|
|
67
67
|
same draft. v3 has no `useConsentManager()`; the
|
|
68
|
-
|
|
68
|
+
`use-consent-manager-to-hooks` codemod in the
|
|
69
|
+
[Inth CLI](https://inth.com/docs/cli)
|
|
69
70
|
rewrites most of its calls to hooks. Read [how consent works](../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice) before
|
|
70
71
|
treating a callback as evidence of a choice.
|
|
@@ -274,7 +274,7 @@ The backend's `additionalConsent` option sets the cache, which defaults to the
|
|
|
274
274
|
|
|
275
275
|
### List the providers in the browser
|
|
276
276
|
|
|
277
|
-
|
|
277
|
+
Local mode has no backend, and some backends do not list providers. In
|
|
278
278
|
those setups, pass `additionalConsent` with your other IAB options: the
|
|
279
279
|
`IABProvider` props in React, Next.js and TanStack Start, the `createIAB`
|
|
280
280
|
options in JavaScript, and the `iab` option of the Vue, Nuxt, Svelte, Astro
|
|
@@ -380,6 +380,13 @@ its own CMP ID. It lists the CMPs it has certified on its
|
|
|
380
380
|
Ask Google to list your CMP ID through its CMP intake form before you rely on
|
|
381
381
|
the AC string in production.
|
|
382
382
|
|
|
383
|
+
## c15t's IAB CMP registration
|
|
384
|
+
|
|
385
|
+
Inth makes c15t. Inth's legal entity, Consent Management Inc., is registered
|
|
386
|
+
with IAB Europe as a CMP under CMP ID 490. The
|
|
387
|
+
[IAB Europe CMP list](https://iabeurope.eu/cmp-list/) shows it under the name
|
|
388
|
+
Consent Management Inc.
|
|
389
|
+
|
|
383
390
|
## How the preference centre shows features
|
|
384
391
|
|
|
385
392
|
The stock IAB preference centres in React, Next.js, Vue, Svelte, Astro and the
|
|
@@ -21,11 +21,11 @@ visitor's location from the request, so regional policies need no extra setup.
|
|
|
21
21
|
[rendering](./rendering.md#react-router-remix-and-other-server-rendered-apps)
|
|
22
22
|
for what that means for the first paint.
|
|
23
23
|
|
|
24
|
-
You need an [Inth](https://inth.com)
|
|
24
|
+
You need an [Inth project](https://inth.com/dashboard/sign-up?framework=react). In the project, set the policy
|
|
25
25
|
rules for your regions, add your site's origin to the trusted origins, and copy
|
|
26
26
|
the backend URL.
|
|
27
27
|
[Choose your setup](../../concepts/choose-your-setup.md#decide-who-runs-the-consent-backend)
|
|
28
|
-
covers self-hosting and
|
|
28
|
+
covers self-hosting and local mode.
|
|
29
29
|
|
|
30
30
|
The guide uses `hosted()` mode. Policy, translation and vendor edits in your
|
|
31
31
|
project reach visitors on their next page load, without a rebuild.
|
|
@@ -54,7 +54,7 @@ usually shows 50 to 250 ms sooner. There is nothing to configure.
|
|
|
54
54
|
|
|
55
55
|
The early request can't read your app's options, so in a few setups it won't
|
|
56
56
|
match and the app sends a second `/init`. Pass `initPrefetch: false` when the
|
|
57
|
-
app uses `
|
|
57
|
+
app uses `local()` or gives `hosted()` its own `backendURL`, `fetch`,
|
|
58
58
|
`initURL` or `headers`. With its own `fetch`, `hosted()` sends `/init` through
|
|
59
59
|
it and ignores the early response. If it sets `credentials`, `overrides`, `storageKey`, an
|
|
60
60
|
`experiment`, or `journey: false`, pass the same values as
|
|
@@ -12,10 +12,10 @@ The banner, dialog, widget and gate placeholder read their text from one set
|
|
|
12
12
|
of translations for the visitor's language. Which set depends on the
|
|
13
13
|
provider's `mode`:
|
|
14
14
|
|
|
15
|
-
| Mode
|
|
16
|
-
|
|
|
17
|
-
| `hosted()`
|
|
18
|
-
| `
|
|
15
|
+
| Mode | Copy comes from |
|
|
16
|
+
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `hosted()` | The backend's `/init` response for the visitor's language, from the translations in your Inth project or your self-hosted backend's configuration, with your `i18n` messages for that language on top |
|
|
18
|
+
| `local()` | Your `i18n` messages, merged over bundled English |
|
|
19
19
|
|
|
20
20
|
With a backend, edit wording in your Inth project first, so it changes without
|
|
21
21
|
a deploy. Code can override that copy in two places:
|
|
@@ -35,12 +35,12 @@ When your project has no copy for it, the backend returns its fallback
|
|
|
35
35
|
language. Set `overrides.language` in the provider options to request a
|
|
36
36
|
specific language instead.
|
|
37
37
|
|
|
38
|
-
In `
|
|
38
|
+
In `local()` mode, `i18n.locale` picks the language, `en` by default. The
|
|
39
39
|
browser's language is not read.
|
|
40
40
|
|
|
41
41
|
The banner, dialog and widget set `dir` from the active language. Right-to-left
|
|
42
|
-
languages such as Arabic and Hebrew render with `dir="rtl"
|
|
43
|
-
|
|
42
|
+
languages such as Arabic and Hebrew render with `dir="rtl"`. The banner stays
|
|
43
|
+
in the same corner.
|
|
44
44
|
|
|
45
45
|
## Change the copy in code
|
|
46
46
|
|
|
@@ -73,7 +73,7 @@ Pass them to the provider in the file that renders it, such as
|
|
|
73
73
|
|
|
74
74
|
For the visitor's language, your messages replace the backend's copy key by
|
|
75
75
|
key. Keys you leave out keep the project's wording, and messages for other
|
|
76
|
-
languages are not applied. In `
|
|
76
|
+
languages are not applied. In `local()` mode c15t bundles only English, so
|
|
77
77
|
keys you leave out for another language show in English.
|
|
78
78
|
|
|
79
79
|
The provider reads `i18n` once, when it mounts. In development it logs a
|
|
@@ -137,7 +137,7 @@ Setting the language without calling `init()` changes nothing on screen. Your
|
|
|
137
137
|
`i18n` messages for the new language apply on top of the copy that arrives.
|
|
138
138
|
Render the switcher anywhere inside `ConsentProvider`.
|
|
139
139
|
|
|
140
|
-
Switching needs a backend. In `
|
|
140
|
+
Switching needs a backend. In `local()` mode the copy stays in the
|
|
141
141
|
`i18n.locale` language.
|
|
142
142
|
|
|
143
143
|
If your site changes language through its own i18n library, you can instead
|
|
@@ -44,7 +44,7 @@ is down, run the build with `C15T_ON_BUILD_ERROR=runtime`. `snapshot` is then
|
|
|
44
44
|
|
|
45
45
|
Only a build that uses `manifest()` downloads the manifest. The plugin
|
|
46
46
|
fetches after Vite has dropped the code the app does not use, so a `hosted()`
|
|
47
|
-
or `
|
|
47
|
+
or `local()` build never contacts the backend and never fails this way.
|
|
48
48
|
|
|
49
49
|
An error or warning that says `no backend URL is set` means the plugin found
|
|
50
50
|
no `backendURL` option and no variable. Set `VITE_C15T_BACKEND_URL` (or
|
|
@@ -18,7 +18,7 @@ Read ./upgrade-v3.md first and follow it in order. Do not guess v3 APIs from mem
|
|
|
18
18
|
1. List every c15t dependency and import: `@c15t/react`, `@c15t/scripts`, `@c15t/dev-tools` and `c15t`.
|
|
19
19
|
2. Check that React is 18 or later. Stop and tell me if it needs a major upgrade.
|
|
20
20
|
3. Replace the packages with `c15t`, plus `@c15t/integrations` if the app uses vendor helpers.
|
|
21
|
-
4.
|
|
21
|
+
4. Install the Inth CLI with `npm install -g @inth/cli` if `inth` is missing, then run `inth c15t codemods packages-to-c15t 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.
|
|
22
22
|
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.
|
|
23
23
|
6. Update callbacks, policy presets, renamed options and components, theme CSS, CSS variables and IAB setup as the guide describes.
|
|
24
24
|
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 with <https://v3.c15t.com/docs/self-host/upgrade-v3.md>.
|
|
@@ -34,6 +34,8 @@ When you finish, list the files you changed, anything you could not migrate, and
|
|
|
34
34
|
client can't read a v2 backend. See
|
|
35
35
|
[Upgrade a self-hosted backend](https://c15t.com/docs/self-host/upgrade-v3).
|
|
36
36
|
Inth-hosted backends need no change.
|
|
37
|
+
* Install the [Inth CLI](https://inth.com/docs/cli/getting-started). The
|
|
38
|
+
codemods run through `inth c15t codemods`.
|
|
37
39
|
|
|
38
40
|
## Replace the packages
|
|
39
41
|
|
|
@@ -111,7 +113,7 @@ ends in v4; versions already published stay on npm.
|
|
|
111
113
|
To preview the import changes in JavaScript and TypeScript files:
|
|
112
114
|
|
|
113
115
|
```bash
|
|
114
|
-
|
|
116
|
+
inth c15t codemods scripts-to-integrations --dry-run --json
|
|
115
117
|
```
|
|
116
118
|
|
|
117
119
|
Review the output, then run it again without `--dry-run`. The codemod runs
|
|
@@ -126,7 +128,7 @@ that called it re-rendered on every change. v3 has one hook per field. Run the
|
|
|
126
128
|
codemod first:
|
|
127
129
|
|
|
128
130
|
```bash
|
|
129
|
-
|
|
131
|
+
inth c15t codemods use-consent-manager-to-hooks --dry-run --json
|
|
130
132
|
```
|
|
131
133
|
|
|
132
134
|
Review the proposed files, then run it again without `--dry-run`. For this v2
|
|
@@ -227,7 +229,7 @@ a transport as `mode`:
|
|
|
227
229
|
| v2 option | v3 option |
|
|
228
230
|
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
229
231
|
| `mode: 'hosted'` with `backendURL` | `mode: hosted({ backendURL })` |
|
|
230
|
-
| `mode: 'offline'` with `offlinePolicy` | `mode:
|
|
232
|
+
| `mode: 'offline'` with `offlinePolicy` | `mode: local({ policyRules })` |
|
|
231
233
|
| `mode: 'custom'` with `endpointHandlers` | `mode: custom(transport)`, where `transport` implements the v3 transport interface. `endpointHandlers` throws. |
|
|
232
234
|
|
|
233
235
|
```tsx title="After (v3): the provider, changed lines"
|
|
@@ -239,12 +241,13 @@ import { ConsentBanner, ConsentDialog, ConsentProvider, hosted } from 'c15t/reac
|
|
|
239
241
|
>
|
|
240
242
|
```
|
|
241
243
|
|
|
242
|
-
The `consent-provider-options` codemod rewrites the hosted and
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
The `consent-provider-options` codemod rewrites the hosted mode and adds the
|
|
245
|
+
`hosted` import. It leaves offline and custom modes as they are, with a
|
|
246
|
+
`TODO(c15t v3)` comment. Move `offlinePolicy.policyPacks` to
|
|
247
|
+
`local({ policyRules })` by hand. It can't translate `endpointHandlers`:
|
|
245
248
|
|
|
246
249
|
```bash
|
|
247
|
-
|
|
250
|
+
inth c15t codemods consent-provider-options --dry-run --json
|
|
248
251
|
```
|
|
249
252
|
|
|
250
253
|
Keep your backend URL. An existing hosted URL such as
|
|
@@ -260,7 +263,7 @@ which bundles the policy when Vite builds. It skips `/init` only when the
|
|
|
260
263
|
policy has no location rules or the page knows the visitor's location. See
|
|
261
264
|
[consent modes](../../concepts/modes.md).
|
|
262
265
|
|
|
263
|
-
|
|
266
|
+
Local mode keeps choices in the browser and records nothing on a server. Not
|
|
264
267
|
recommended for production environments.
|
|
265
268
|
|
|
266
269
|
If your app renders on the server with React Router or Remix, the banner still
|
|
@@ -285,7 +288,7 @@ payload, and on `onConsentSet` and `onBannerFetched`, which it can't choose a
|
|
|
285
288
|
replacement for:
|
|
286
289
|
|
|
287
290
|
```bash
|
|
288
|
-
|
|
291
|
+
inth c15t codemods callbacks-to-v3 --dry-run --json
|
|
289
292
|
```
|
|
290
293
|
|
|
291
294
|
v2's `onConsentChanged` fired only when a save changed at least one category's
|
|
@@ -329,12 +332,12 @@ backend:
|
|
|
329
332
|
change a hosted policy.
|
|
330
333
|
* **Self-hosted**: move the backend's `policyPacks` to `manifest.policyRules`.
|
|
331
334
|
See [policy configuration](https://c15t.com/docs/self-host/guides/policy-packs).
|
|
332
|
-
* **
|
|
335
|
+
* **No backend**: move `offlinePolicy.policyPacks` to `local({ policyRules })`.
|
|
333
336
|
|
|
334
|
-
```tsx title="After (v3):
|
|
335
|
-
import {
|
|
337
|
+
```tsx title="After (v3): local policy rules"
|
|
338
|
+
import { local, policyRulePresets } from 'c15t';
|
|
336
339
|
|
|
337
|
-
const mode =
|
|
340
|
+
const mode = local({
|
|
338
341
|
policyRules: [
|
|
339
342
|
policyRulePresets.europeOptIn(),
|
|
340
343
|
policyRulePresets.worldOptOutNoPrompt(),
|
|
@@ -342,7 +345,7 @@ const mode = offline({
|
|
|
342
345
|
});
|
|
343
346
|
```
|
|
344
347
|
|
|
345
|
-
In Next.js, import `
|
|
348
|
+
In Next.js, import `local` from `c15t/next` instead and set the result as
|
|
346
349
|
`mode` in `c15t.config.ts`. Both take the same `policyRules`.
|
|
347
350
|
|
|
348
351
|
| v2 preset | v3 preset |
|
|
@@ -357,13 +360,14 @@ rights, while `worldOptOutNoPrompt()` keeps v2's opt-out and preference rights.
|
|
|
357
360
|
|
|
358
361
|
The `policy-packs-to-policy-rules` codemod renames `policyPackPresets` and
|
|
359
362
|
`worldNoBanner()`, and moves the import to `c15t` where it came from
|
|
360
|
-
`c15t/react` or `c15t/next`. `consent-provider-options`
|
|
361
|
-
|
|
363
|
+
`c15t/react` or `c15t/next`. `consent-provider-options` leaves offline mode
|
|
364
|
+
with a `TODO(c15t v3)` comment; move `offlinePolicy.policyPacks` into
|
|
365
|
+
`offline({ policyRules })` by hand. Preset calls need
|
|
362
366
|
no other change, but a hand-written v2 pack uses `consent` and `ui` keys,
|
|
363
367
|
which v3 rules replace with flat keys such as `model` and `prompt`; the
|
|
364
368
|
codemods mark those with a `TODO(c15t v3)` comment.
|
|
365
369
|
|
|
366
|
-
v3 adds presets for more regions. `
|
|
370
|
+
v3 adds presets for more regions. `local()` without `policyRules` uses
|
|
367
371
|
`recommendedPolicyRules()`. Read [policies](../../concepts/policies.md) before you
|
|
368
372
|
change a rule's model or prompt, and
|
|
369
373
|
[how consent works](../../concepts/how-consent-works.md) for the difference
|
|
@@ -392,13 +396,13 @@ between a permission and a recorded choice.
|
|
|
392
396
|
| `iab: iab({ ... })` | `IABProvider`. See [Move IAB setup](#move-iab-setup-to-iabprovider). |
|
|
393
397
|
|
|
394
398
|
The `consent-provider-options` codemod renames `ConsentManagerProvider` and
|
|
395
|
-
its option and prop types, turns `mode` and `backendURL` into
|
|
396
|
-
`
|
|
397
|
-
|
|
398
|
-
to `i18n
|
|
399
|
+
its option and prop types, turns `mode: 'hosted'` and `backendURL` into
|
|
400
|
+
`hosted()`, and renames `iframeBlockerConfig`. It leaves offline and custom
|
|
401
|
+
modes with a `TODO(c15t v3)` comment. v2 still accepted the deprecated
|
|
402
|
+
`translations` option; rename it to `i18n` by hand.
|
|
399
403
|
|
|
400
404
|
```bash
|
|
401
|
-
|
|
405
|
+
inth c15t codemods consent-provider-options --dry-run --json
|
|
402
406
|
```
|
|
403
407
|
|
|
404
408
|
`ConsentBanner`, `ConsentDialog`, `ConsentDialogLink`, `ConsentDialogTrigger`
|
|
@@ -426,7 +430,7 @@ The `root-exports-to-subpaths` codemod moves these imports to their new
|
|
|
426
430
|
entries and marks removed names with a `TODO(c15t v3)` comment:
|
|
427
431
|
|
|
428
432
|
```bash
|
|
429
|
-
|
|
433
|
+
inth c15t codemods root-exports-to-subpaths --dry-run --json
|
|
430
434
|
```
|
|
431
435
|
|
|
432
436
|
`ConsentGate` changes one behavior from `Frame`. When its category is granted,
|
|
@@ -498,7 +502,7 @@ The `css-variables-to-v3` codemod renames `--consent-widget-*` and
|
|
|
498
502
|
`theme.slots.consentDialogFooter` and marks `theme.slots.frame`.
|
|
499
503
|
|
|
500
504
|
```bash
|
|
501
|
-
|
|
505
|
+
inth c15t codemods theme-to-consent-theme css-variables-to-v3 postcss-tailwind3 --dry-run --json
|
|
502
506
|
```
|
|
503
507
|
|
|
504
508
|
`theme.slots` now applies in React. v2 accepted it in types and ignored it, so
|
|
@@ -586,7 +590,7 @@ The `dev-tools-to-c15t` codemod points the imports at `c15t/react/devtools` and
|
|
|
586
590
|
removes the `namespace` prop:
|
|
587
591
|
|
|
588
592
|
```bash
|
|
589
|
-
|
|
593
|
+
inth c15t codemods dev-tools-to-c15t --dry-run --json
|
|
590
594
|
```
|
|
591
595
|
|
|
592
596
|
## Upgrade from an earlier v3 alpha
|
|
@@ -622,16 +626,19 @@ handled by the `consent-provider-options` codemod, not by aliases.
|
|
|
622
626
|
| The build options `outputFile`, `exportName`, `importSource` and `rootDir` | Removed. The snapshot is a virtual module, or a file under `node_modules/.cache/c15t/` in Next.js. |
|
|
623
627
|
|
|
624
628
|
React removes nothing besides the modes above. Import `manifest()` from
|
|
625
|
-
`c15t/react`, next to `hosted()` and `
|
|
629
|
+
`c15t/react`, next to `hosted()` and `local()`, instead of from
|
|
626
630
|
`@c15t/browser/headless`; React apps need nothing from `@c15t/browser`.
|
|
627
631
|
|
|
628
632
|
## Update the CLI
|
|
629
633
|
|
|
630
|
-
*
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
*
|
|
634
|
+
* Setup moved to the Inth CLI.
|
|
635
|
+
[Install the Inth CLI](https://inth.com/docs/cli/getting-started), then run
|
|
636
|
+
`inth c15t` in place of `c15t setup` and `inth c15t codemods` in place of
|
|
637
|
+
`c15t codemods`.
|
|
638
|
+
* Sign in with `inth login`. Sessions saved by the v2 CLI aren't read.
|
|
639
|
+
* `inth c15t scaffold` writes the backend URL to `.env`, not `.env.local` or
|
|
640
|
+
`.env.example`.
|
|
641
|
+
* Remix and Gatsby have no CLI support. Pick a guide from
|
|
635
642
|
[Choose your setup](../../concepts/choose-your-setup.md) for those apps.
|
|
636
643
|
|
|
637
644
|
## Check the migration
|