@c15t/react 3.0.0-alpha.3 → 3.0.0-alpha.5
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 +92 -72
- package/README.md +21 -17
- package/SKILL.md +34 -0
- package/dist/component-hooks/use-headless-consent-ui.js +1 -1
- package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
- package/dist/components/consent-gate/atoms.js +1 -1
- package/dist/components/consent-gate/consent-gate.js +1 -1
- package/dist/components/iab-panel/atoms/card.js +1 -1
- package/dist/components/iab-panel/atoms/feature-item.js +2 -0
- package/dist/components/iab-panel/atoms/header.js +1 -1
- package/dist/components/iab-panel/iab-panel.js +1 -1
- package/dist/components/iab-panel/index.js +1 -1
- package/dist/components/iab-prompt/atoms/overlay.js +1 -1
- package/dist/components/iab-prompt/atoms/root.js +1 -1
- package/dist/components/panel/atoms/card.js +1 -1
- package/dist/components/panel/atoms/root.js +1 -1
- package/dist/components/panel-trigger/atoms/button.js +1 -1
- package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
- package/dist/components/preferences/atoms/accordion.js +1 -1
- package/dist/components/prompt/atoms/overlay.js +1 -1
- package/dist/components/prompt/atoms/root.js +1 -1
- package/dist/components/prompt/components.js +1 -1
- package/dist/components/shared/libs/compose-refs.js +1 -0
- package/dist/components/shared/libs/slot.js +1 -1
- package/dist/components/shared/primitives/button.js +1 -1
- package/dist/components/shared/ui/dialog/dialog.js +1 -1
- package/dist/components/shared/ui/icon.js +1 -1
- package/dist/context/consent-tracking-context.js +1 -1
- package/dist/devtools.js +1 -1
- package/dist/draft-context.js +2 -0
- package/dist/draft.js +1 -1
- package/dist/external-iab-context.js +1 -1
- package/dist/gpp.js +2 -0
- package/dist/hooks/use-color-scheme.js +1 -1
- package/dist/hooks/use-focus-trap.js +1 -1
- package/dist/hooks.js +1 -1
- package/dist/iab/styles.tw3.css +4 -4
- package/dist/index-parts/consent-banner.js +1 -0
- package/dist/index-parts/consent-dialog-link.js +1 -0
- package/dist/index-parts/consent-dialog-trigger.js +1 -0
- package/dist/index-parts/consent-dialog.js +1 -0
- package/dist/index-parts/consent-gate.js +1 -0
- package/dist/index-parts/core.js +1 -0
- package/dist/index-parts/draft.js +1 -0
- package/dist/index-parts/hooks.js +1 -0
- package/dist/index-parts/module-hooks.js +1 -0
- package/dist/index-parts/provider.js +1 -0
- package/dist/index-parts/theme.js +1 -0
- package/dist/index-parts/translations.js +1 -0
- package/dist/index.js +1 -1
- package/dist/kernel-selector.js +1 -1
- package/dist/module-hooks/iframe-blocker.js +1 -1
- package/dist/postcss-tailwind3.js +1 -0
- package/dist/provider.js +1 -1
- package/dist/server/fetch-ssr-data.js +1 -1
- package/dist/server/headers.js +1 -1
- package/dist/server/normalize-url.js +1 -1
- package/dist/styles.tw3.css +16 -16
- package/dist/transports/offline.js +1 -1
- package/dist/utils/merge-slot-props.js +1 -1
- package/dist/utils/theme-utils.js +1 -1
- package/dist/version.js +1 -1
- package/dist-types/component-hooks/use-headless-consent-ui.d.ts +1 -1
- package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
- package/dist-types/components/iab-panel/iab-panel.d.ts +2 -2
- package/dist-types/components/iab-panel/index.d.ts +5 -2
- package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
- package/dist-types/components/iab-prompt/iab-prompt.d.ts +2 -2
- package/dist-types/components/iab-prompt/index.d.ts +2 -2
- package/dist-types/components/panel/atoms/card.d.ts +1 -0
- package/dist-types/components/prompt/components.d.ts +6 -0
- package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
- package/dist-types/components/shared/ui/icon.d.ts +2 -1
- package/dist-types/context/consent-tracking-context.d.ts +10 -0
- package/dist-types/context/theme-context.d.ts +2 -2
- package/dist-types/context.d.ts +6 -0
- package/dist-types/devtools.d.ts +13 -2
- package/dist-types/draft-context.d.ts +31 -0
- package/dist-types/draft.d.ts +13 -30
- package/dist-types/gpp.d.ts +27 -0
- package/dist-types/hooks/use-focus-trap.d.ts +3 -1
- package/dist-types/hooks.d.ts +57 -13
- package/dist-types/iab.d.ts +1 -1
- package/dist-types/index-parts/consent-banner.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog.d.ts +7 -0
- package/dist-types/index-parts/consent-gate.d.ts +7 -0
- package/dist-types/index-parts/core.d.ts +8 -0
- package/dist-types/index-parts/draft.d.ts +7 -0
- package/dist-types/index-parts/hooks.d.ts +7 -0
- package/dist-types/index-parts/module-hooks.d.ts +7 -0
- package/dist-types/index-parts/provider.d.ts +7 -0
- package/dist-types/index-parts/theme.d.ts +8 -0
- package/dist-types/index-parts/translations.d.ts +7 -0
- package/dist-types/index.d.ts +14 -15
- package/dist-types/kernel-selector.d.ts +0 -8
- package/dist-types/module-hooks/iframe-blocker.d.ts +12 -7
- package/dist-types/postcss-tailwind3.d.ts +21 -0
- package/dist-types/provider.d.ts +57 -17
- package/dist-types/server/headers.d.ts +14 -4
- package/dist-types/server/normalize-url.d.ts +14 -7
- package/dist-types/server/types.d.ts +12 -1
- package/dist-types/transports/offline.d.ts +7 -16
- package/dist-types/ui-config-context.d.ts +7 -1
- package/dist-types/utils/merge-slot-props.d.ts +15 -1
- package/dist-types/utils/theme-utils.d.ts +17 -2
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +92 -72
- package/docs/assets/v3/bottom-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/brand-preferences.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/headless-bar-html.png +0 -0
- package/docs/assets/v3/headless-bar-mobile.png +0 -0
- package/docs/assets/v3/headless-bar.png +0 -0
- package/docs/assets/v3/slim-bar.png +0 -0
- package/docs/concepts/choose-your-setup.md +87 -0
- package/docs/{frameworks/react/concepts → concepts}/consent-categories.md +22 -7
- package/docs/{guides → concepts}/consent-state.md +89 -105
- package/docs/{guides → concepts}/data-fetching.md +31 -27
- package/docs/concepts/how-consent-works.md +123 -0
- package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +7 -59
- package/docs/customization/class-names.md +202 -0
- package/docs/customization/dark-mode.md +157 -0
- package/docs/customization/motion.md +119 -0
- package/docs/customization/overview.md +67 -34
- package/docs/customization/recipes.md +839 -49
- package/docs/customization/slots.md +216 -35
- package/docs/customization/stylesheets.md +147 -0
- package/docs/customization/tailwind.md +842 -0
- package/docs/customization/tokens.md +163 -96
- package/docs/customization/translations.md +60 -3
- package/docs/frameworks/react/callbacks.md +118 -0
- package/docs/frameworks/react/clear-on-revocation.md +106 -0
- package/docs/frameworks/react/components/consent-banner.md +42 -28
- package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
- package/docs/frameworks/react/components/consent-dialog-trigger.md +81 -76
- package/docs/frameworks/react/components/consent-dialog.md +56 -50
- package/docs/frameworks/react/components/consent-gate.md +37 -20
- package/docs/frameworks/react/components/consent-provider.md +130 -0
- package/docs/frameworks/react/components/consent-widget.md +23 -20
- package/docs/frameworks/react/components/dev-tools.md +53 -85
- package/docs/frameworks/react/components.md +105 -0
- package/docs/frameworks/react/compose.md +354 -0
- package/docs/frameworks/react/content-security-policy.md +126 -0
- package/docs/frameworks/react/customize.md +250 -0
- package/docs/frameworks/react/embeds.md +89 -0
- package/docs/frameworks/react/gpp.md +163 -0
- package/docs/frameworks/react/{hooks/overview.md → hooks.md} +21 -16
- package/docs/frameworks/react/{iab/overview.md → iab.md} +80 -31
- package/docs/frameworks/react/network-blocker.md +35 -41
- package/docs/frameworks/react/quickstart.md +139 -101
- package/docs/frameworks/react/rendering.md +128 -0
- package/docs/frameworks/react/scripts.md +115 -0
- package/docs/frameworks/react/translations.md +155 -0
- package/docs/frameworks/react/troubleshooting.md +92 -47
- package/docs/frameworks/react/vendor-consent.md +170 -0
- package/docs/guides/banner-experiments.md +654 -0
- package/docs/guides/troubleshooting.md +120 -47
- package/docs/guides/verify-consent.md +81 -49
- package/docs/integrations/adobe-analytics.md +167 -159
- package/docs/integrations/ahrefs-analytics.md +152 -154
- package/docs/integrations/amplitude.md +162 -156
- package/docs/integrations/building-integrations.md +136 -37
- package/docs/integrations/clearbit.md +154 -154
- package/docs/integrations/cloudflare-web-analytics.md +156 -156
- package/docs/integrations/cloudflare-zaraz.md +209 -261
- package/docs/integrations/crisp.md +164 -158
- package/docs/integrations/databuddy.md +157 -173
- package/docs/integrations/fathom-analytics.md +158 -156
- package/docs/integrations/front-chat.md +167 -167
- package/docs/integrations/google-maps.md +118 -83
- package/docs/integrations/google-tag-manager.md +178 -163
- package/docs/integrations/google-tag.md +163 -160
- package/docs/integrations/heap.md +163 -155
- package/docs/integrations/hightouch.md +161 -157
- package/docs/integrations/hotjar.md +159 -155
- package/docs/integrations/intercom.md +183 -153
- package/docs/integrations/klaviyo.md +486 -0
- package/docs/integrations/linkedin-insights.md +174 -150
- package/docs/integrations/logrocket.md +160 -156
- package/docs/integrations/matomo-analytics.md +188 -178
- package/docs/integrations/meta-pixel.md +188 -150
- package/docs/integrations/microsoft-clarity.md +163 -155
- package/docs/integrations/microsoft-uet.md +148 -154
- package/docs/integrations/mixpanel-analytics.md +155 -160
- package/docs/integrations/one-dollar-stats.md +166 -165
- package/docs/integrations/openai-pixel.md +204 -301
- package/docs/integrations/overview.md +137 -80
- package/docs/integrations/pinterest-tag.md +191 -183
- package/docs/integrations/pirsch.md +169 -159
- package/docs/integrations/plausible-analytics.md +172 -158
- package/docs/integrations/posthog.md +313 -240
- package/docs/integrations/promptwatch.md +154 -154
- package/docs/integrations/reddit-pixel.md +185 -157
- package/docs/integrations/rudderstack.md +201 -186
- package/docs/integrations/rybbit-analytics.md +171 -160
- package/docs/integrations/segment.md +182 -154
- package/docs/integrations/snapchat-pixel.md +186 -156
- package/docs/integrations/tiktok-pixel.md +171 -150
- package/docs/integrations/umami-analytics.md +163 -158
- package/docs/integrations/vercel-analytics.md +166 -157
- package/docs/integrations/x-pixel.md +176 -150
- package/docs/integrations/youtube.md +121 -86
- package/docs/upgrade-v3.md +496 -467
- package/package.json +20 -9
- package/dist/ui-save.js +0 -1
- package/dist-types/ui-save.d.ts +0 -25
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/frameworks/react/components/consent-manager-provider.md +0 -95
- package/docs/frameworks/react/script-loader.md +0 -69
- package/docs/frameworks/react/styling/overview.md +0 -266
- package/docs/guides/deployment-modes.md +0 -75
- package/docs/guides/shared-consent-controls.md +0 -158
- package/docs/integrations/clear-on-revocation.md +0 -167
- package/docs/integrations/granular-consent.md +0 -210
|
@@ -11,6 +11,12 @@ Use the IAB integration when your application needs the Transparency and Consent
|
|
|
11
11
|
Framework. Configure an IAB policy, the correct CMP identity and vendor data for
|
|
12
12
|
your deployment. A demo CMP ID is not a production configuration.
|
|
13
13
|
|
|
14
|
+
A CMP ID is assigned to a company when it registers as a CMP with IAB Europe
|
|
15
|
+
([register.consensu.org/CMP](https://register.consensu.org/CMP)). Every TC
|
|
16
|
+
String names the CMP that wrote it, so an ID you did not register attributes
|
|
17
|
+
your users' consent records to someone else. Omitting it leaves the add-on
|
|
18
|
+
unmounted: no `__tcfapi`, and no TC String written.
|
|
19
|
+
|
|
14
20
|
Install `@c15t/iab` alongside your framework adapter, then mount the IAB
|
|
15
21
|
components inside an existing consent provider connected to Inth.
|
|
16
22
|
|
|
@@ -21,15 +27,19 @@ stylesheet holds only the IAB rules and uses the tokens from the base
|
|
|
21
27
|
stylesheet.
|
|
22
28
|
|
|
23
29
|
```tsx title="src/iab-consent.tsx"
|
|
24
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
IABProvider,
|
|
32
|
+
IABConsentBanner,
|
|
33
|
+
IABConsentDialog,
|
|
34
|
+
} from 'c15t/react/iab';
|
|
25
35
|
|
|
26
36
|
export function IABConsent({ cmpId }: { cmpId: number }) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
return (
|
|
38
|
+
<IABProvider cmpId={cmpId}>
|
|
39
|
+
<IABConsentBanner />
|
|
40
|
+
<IABConsentDialog />
|
|
41
|
+
</IABProvider>
|
|
42
|
+
);
|
|
33
43
|
}
|
|
34
44
|
```
|
|
35
45
|
|
|
@@ -44,7 +54,7 @@ Global Vendor List response before changing rendering conditions.
|
|
|
44
54
|
|
|
45
55
|
IAB presentation follows `presentation.prompt.blocking` and
|
|
46
56
|
`presentation.preferences.blocking`. A blocking dialog needs focus management,
|
|
47
|
-
scroll locking and a backdrop together. See [migration](
|
|
57
|
+
scroll locking and a backdrop together. See [migration](../../upgrade-v3.md) for
|
|
48
58
|
legacy option behavior.
|
|
49
59
|
|
|
50
60
|
## Verify vendor and purpose choices
|
|
@@ -62,22 +72,26 @@ placeholders; use vendors from your list whose declarations allow each
|
|
|
62
72
|
restriction.
|
|
63
73
|
|
|
64
74
|
```tsx title="src/iab-consent.tsx"
|
|
65
|
-
import {
|
|
75
|
+
import {
|
|
76
|
+
IABProvider,
|
|
77
|
+
IABConsentBanner,
|
|
78
|
+
IABConsentDialog,
|
|
79
|
+
} from 'c15t/react/iab';
|
|
66
80
|
|
|
67
81
|
const publisherRestrictions = [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
// Vendors 10 and 755 may not use purpose 4.
|
|
83
|
+
{ purposeId: 4, restrictionType: 0 as const, vendorIds: [10, 755] },
|
|
84
|
+
// Vendor 755 must use legitimate interest for purpose 7.
|
|
85
|
+
{ purposeId: 7, restrictionType: 2 as const, vendorIds: [755] },
|
|
72
86
|
];
|
|
73
87
|
|
|
74
88
|
export function IABConsent({ cmpId }: { cmpId: number }) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
return (
|
|
90
|
+
<IABProvider cmpId={cmpId} publisherRestrictions={publisherRestrictions}>
|
|
91
|
+
<IABConsentBanner />
|
|
92
|
+
<IABConsentDialog />
|
|
93
|
+
</IABProvider>
|
|
94
|
+
);
|
|
81
95
|
}
|
|
82
96
|
```
|
|
83
97
|
|
|
@@ -90,8 +104,9 @@ Each restriction type has a requirement that depends on the vendor list:
|
|
|
90
104
|
| `2` | Legitimate interest required | declares the purpose for consent and lists it in `flexiblePurposes` |
|
|
91
105
|
|
|
92
106
|
Type `2` is never allowed for purposes 1, 3, 4, 5 and 6, which TCF allows
|
|
93
|
-
only with consent.
|
|
94
|
-
|
|
107
|
+
only with consent. TCF allows restrictions only in service-specific TC strings.
|
|
108
|
+
c15t always writes those, so the deprecated `isServiceSpecific` option has no
|
|
109
|
+
effect on restrictions.
|
|
95
110
|
c15t copies the restrictions when the CMP starts, so changing your array
|
|
96
111
|
afterwards has no effect.
|
|
97
112
|
|
|
@@ -140,7 +155,7 @@ objection control is the opt-out. Such a purpose does not decide its c15t
|
|
|
140
155
|
category: the category follows the purposes in it that the visitor can
|
|
141
156
|
consent to. Legitimate interest never grants a category on its own. If none of
|
|
142
157
|
a category's purposes has a consent basis, the category needs all of them
|
|
143
|
-
consented, which only Accept
|
|
158
|
+
consented, which only Accept All does. Stack switches
|
|
144
159
|
cover only the purposes some vendor processes on consent. Display-model rows
|
|
145
160
|
expose this as `hasConsentBasis`. Custom preference UIs can call
|
|
146
161
|
`applyPublisherRestrictionsToGVL` from `@c15t/iab/headless`, or pass
|
|
@@ -162,12 +177,46 @@ Types `1` and `2` block the target when the vendor list does not mark the
|
|
|
162
177
|
purpose as flexible for that vendor. Targets without a `vendorId` ignore
|
|
163
178
|
restrictions. A target that uses only legitimate interest once restrictions
|
|
164
179
|
apply is not blocked by a refused category, since it needs no consent; its
|
|
165
|
-
legitimate interest signals and the visitor's objection decide. GPC
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
it
|
|
172
|
-
|
|
173
|
-
|
|
180
|
+
legitimate interest signals and the visitor's objection decide. GPC and strict
|
|
181
|
+
scope still block it, and the category stays refused for scripts that depend on
|
|
182
|
+
it. Accept All grants the vendor signal a restriction moves a purpose to,
|
|
183
|
+
including legitimate interest for a vendor that declares none. Legitimate
|
|
184
|
+
interest a restriction introduces applies until the visitor objects: the
|
|
185
|
+
preference centres show it as allowed, and saving without touching it encodes
|
|
186
|
+
it as allowed. `introducedLegitimateInterest` from `@c15t/iab/headless` lists
|
|
187
|
+
those purposes and vendors for custom preference UIs.
|
|
188
|
+
|
|
189
|
+
## How the preference centre shows features
|
|
190
|
+
|
|
191
|
+
The stock IAB preference centres in React, Next.js, Vue, Svelte, Astro and the
|
|
192
|
+
script tag follow TCF 2.4 and Policies v5.0.b. Special purposes stay in their
|
|
193
|
+
locked section with a lock icon, because the visitor cannot object to them.
|
|
194
|
+
|
|
195
|
+
Features get their own section after the special purposes. It shows the IAB
|
|
196
|
+
standard text for features from the Global Vendor List's
|
|
197
|
+
`standardTexts.features`. If the list has no standard text, it shows the
|
|
198
|
+
`iab.preferenceCenter.features.description` translation. Each feature lists
|
|
199
|
+
its name, description, illustrations and the vendors that use it. The section has no switch, lock or vendor toggle, because
|
|
200
|
+
TCF forbids showing features next to a control that cannot be disabled.
|
|
201
|
+
|
|
202
|
+
The feature section follows the theme's spacing and typography tokens.
|
|
203
|
+
Its disclosure arrows are decorative and stay out of the accessibility tree.
|
|
204
|
+
|
|
205
|
+
A custom preference UI gets the same rows from
|
|
206
|
+
`resolveIABDialogDisplayModel` in `@c15t/iab/headless`. `essentialRows` holds
|
|
207
|
+
the special purposes only. `featureRows` holds the features, with
|
|
208
|
+
`locked: false` and `toggle: 'none'`, so render them without a control.
|
|
209
|
+
`featuresStandardText` is the standard text, or `null` when you should use the
|
|
210
|
+
translation.
|
|
211
|
+
|
|
212
|
+
## Consent scope
|
|
213
|
+
|
|
214
|
+
c15t stores TCF choices per site and browser, which the TCF calls
|
|
215
|
+
service-specific scope. It does not sync TC strings across devices, and every
|
|
216
|
+
TC string it writes has `IsServiceSpecific` set to `1`.
|
|
217
|
+
|
|
218
|
+
Do not use `/consent/check` or your own account data to skip the TCF banner on
|
|
219
|
+
another device. That turns the choice into multi-device scope, which the TCF
|
|
220
|
+
requires you to disclose in the first layer, and c15t does not show that
|
|
221
|
+
disclosure. The `isServiceSpecific` option is deprecated. c15t ignores it and
|
|
222
|
+
logs a warning when you pass `false`.
|
|
@@ -1,50 +1,42 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
description:
|
|
4
|
-
|
|
2
|
+
title: Network blocker
|
|
3
|
+
description: Hold fetch and XMLHttpRequest calls in a React app until their
|
|
4
|
+
consent category is allowed, with networkBlocker rules in the ConsentProvider
|
|
5
|
+
options.
|
|
5
6
|
group: frameworks
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
## Add rules
|
|
9
|
+
## Add rules
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
calls
|
|
13
|
-
|
|
11
|
+
Some SDKs are already on the page and send beacons or API calls of their own.
|
|
12
|
+
Add `networkBlocker` rules to the provider options to stop `fetch` and
|
|
13
|
+
`XMLHttpRequest` calls to those domains until the category is allowed. Load
|
|
14
|
+
vendor SDKs through [`scripts`](./scripts.md) first, so they
|
|
15
|
+
do not run at all before consent.
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
supplied by your Inth project.
|
|
17
|
+
This partial example adds the option next to the existing `mode` and
|
|
18
|
+
`scripts`:
|
|
18
19
|
|
|
19
|
-
```tsx title="
|
|
20
|
-
'use client';
|
|
21
|
-
|
|
22
|
-
import type { ReactNode } from 'react';
|
|
23
|
-
import { ConsentBanner, ConsentDialog, ConsentProvider, hosted } from 'c15t/react';
|
|
24
|
-
|
|
25
|
-
const mode = hosted({ url: 'https://your-project.inth.app' });
|
|
20
|
+
```tsx title="src/consent.tsx"
|
|
26
21
|
const networkBlocker = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
rules: [
|
|
23
|
+
{
|
|
24
|
+
id: 'google-analytics',
|
|
25
|
+
domain: 'google-analytics.com',
|
|
26
|
+
category: 'measurement',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'meta-pixel',
|
|
30
|
+
domain: 'facebook.com',
|
|
31
|
+
pathIncludes: '/tr',
|
|
32
|
+
category: 'marketing',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
31
35
|
};
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<ConsentProvider options={{ mode, networkBlocker }}>
|
|
36
|
-
{children}
|
|
37
|
-
<ConsentBanner />
|
|
38
|
-
<ConsentDialog />
|
|
39
|
-
</ConsentProvider>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
37
|
+
<ConsentProvider options={{ mode, scripts, networkBlocker }}>
|
|
42
38
|
```
|
|
43
39
|
|
|
44
|
-
Render your app inside `ConsentProvider`. Blocking starts when it renders, so
|
|
45
|
-
components outside it that send requests while rendering, or modules evaluated
|
|
46
|
-
before it, are not covered. See the limits below.
|
|
47
|
-
|
|
48
40
|
## Match requests with rules
|
|
49
41
|
|
|
50
42
|
Each rule names a `domain` and the consent `category` a request needs. The
|
|
@@ -137,10 +129,12 @@ Keep tracking calls out of that window:
|
|
|
137
129
|
* Check `useConsent('measurement')` (or the category you need) before you
|
|
138
130
|
call a vendor from your own code, and treat the blocker as a backstop.
|
|
139
131
|
|
|
140
|
-
## Verify
|
|
132
|
+
## Verify the blocked requests
|
|
133
|
+
|
|
134
|
+
Open DevTools Network in a private window, under a policy that asks for
|
|
135
|
+
consent.
|
|
141
136
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
check that they
|
|
146
|
-
[verification guide](../../guides/verify-consent.md) covers automated checks.
|
|
137
|
+
1. Before a choice, requests matching a rule are absent, and the console logs
|
|
138
|
+
each blocked request.
|
|
139
|
+
2. Allow the rule's category and save. Matching requests go out.
|
|
140
|
+
3. Reject, reload, and check that they stay absent.
|
|
@@ -1,143 +1,181 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Quickstart
|
|
3
|
-
description:
|
|
4
|
-
|
|
3
|
+
description: Add a consent banner, preferences dialog and consent-gated scripts
|
|
4
|
+
to a React single-page app built with Vite, using Inth as the consent backend.
|
|
5
5
|
group: frameworks
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Before you start
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
framework's adapter for request prefetch.
|
|
10
|
+
This guide sets up a client-rendered React app built with Vite. The browser
|
|
11
|
+
loads the page, asks the consent backend for the visitor's policy, then shows
|
|
12
|
+
the banner or runs the scripts the visitor allowed.
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
* **Next.js or TanStack Start?** Use that framework's guide from the
|
|
15
|
+
[framework list](https://c15t.com/docs/frameworks). Their adapters resolve consent on the
|
|
16
|
+
server.
|
|
17
|
+
* **React Router framework mode, Remix or another server-rendered React app?**
|
|
18
|
+
This guide still works. The server renders the page without consent state and
|
|
19
|
+
the browser resolves it after hydration. Read
|
|
20
|
+
[rendering](./rendering.md#react-router-remix-and-other-server-rendered-apps)
|
|
21
|
+
for what that means for the first paint.
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
You need an [Inth](https://inth.com) project. In the project, set the policy
|
|
24
|
+
rules for your regions, add your site's origin to the trusted origins, and copy
|
|
25
|
+
the backend URL.
|
|
26
|
+
[Choose your setup](../../concepts/choose-your-setup.md#decide-who-runs-the-consent-backend)
|
|
27
|
+
covers self-hosting and offline mode.
|
|
25
28
|
|
|
26
|
-
##
|
|
29
|
+
## Install c15t
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
| Package manager | Command |
|
|
32
|
+
| :-------------- | :------------------------------------------------ |
|
|
33
|
+
| npm | `npm install c15t@alpha @c15t/integrations@alpha` |
|
|
34
|
+
| pnpm | `pnpm add c15t@alpha @c15t/integrations@alpha` |
|
|
35
|
+
| yarn | `yarn add c15t@alpha @c15t/integrations@alpha` |
|
|
36
|
+
| bun | `bun add c15t@alpha @c15t/integrations@alpha` |
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
`c15t` contains the React adapter at `c15t/react`. `@c15t/integrations` holds the
|
|
39
|
+
vendor loaders used below.
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
VITE_C15T_BACKEND_URL=
|
|
36
|
-
VITE_POSTHOG_KEY=
|
|
37
|
-
VITE_POSTHOG_HOST=
|
|
38
|
-
VITE_X_PIXEL_ID=
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Copy the exact backend endpoint from Inth. The vendor IDs are optional: leaving
|
|
42
|
-
an ID empty disables that integration. When enabling PostHog, fill in both its project key and the API host
|
|
43
|
-
shown in your PostHog project. Restart Vite after editing these values and
|
|
44
|
-
rebuild for deployment. Other bundlers need their own configuration mechanism.
|
|
41
|
+
## Register your vendor scripts
|
|
45
42
|
|
|
46
|
-
Create `src/scripts.ts`. PostHog waits for measurement permission
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
Create `src/scripts.ts`. PostHog waits for measurement permission and X Pixel
|
|
44
|
+
waits for marketing permission, so your policy must include both categories.
|
|
45
|
+
Remove any other PostHog or X Pixel loader from your app so each vendor loads
|
|
46
|
+
once.
|
|
49
47
|
|
|
50
48
|
```ts title="src/scripts.ts"
|
|
51
|
-
import { posthog } from '@c15t/
|
|
52
|
-
import { xPixel } from '@c15t/
|
|
53
|
-
|
|
54
|
-
const posthogKey = import.meta.env.VITE_POSTHOG_KEY;
|
|
55
|
-
const xPixelId = import.meta.env.VITE_X_PIXEL_ID;
|
|
49
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
50
|
+
import { xPixel } from '@c15t/integrations/x-pixel';
|
|
56
51
|
|
|
57
52
|
export const scripts = [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
loadMode: 'after-consent',
|
|
65
|
-
}),
|
|
66
|
-
]
|
|
67
|
-
: []),
|
|
68
|
-
...(xPixelId ? [xPixel({ pixelId: xPixelId })] : []),
|
|
53
|
+
posthog({
|
|
54
|
+
id: 'phc_your_project_key',
|
|
55
|
+
initOptions: { cookieless_mode: 'never' },
|
|
56
|
+
loadMode: 'after-consent',
|
|
57
|
+
}),
|
|
58
|
+
xPixel({ pixelId: 'your-pixel-id' }),
|
|
69
59
|
];
|
|
70
60
|
```
|
|
71
61
|
|
|
72
|
-
|
|
62
|
+
Replace `phc_your_project_key` with your PostHog project key and
|
|
63
|
+
`your-pixel-id` with your X Pixel ID. PostHog uses its EU host; add
|
|
64
|
+
`region: 'us'` for a US project. Remove the loader for a vendor you don't use.
|
|
65
|
+
|
|
66
|
+
`loadMode: 'after-consent'` keeps the PostHog SDK off the page until the
|
|
67
|
+
visitor allows measurement. `cookieless_mode: 'never'` turns off PostHog's
|
|
68
|
+
cookieless capture. See [integrations](../../integrations/overview.md) for other
|
|
69
|
+
vendors.
|
|
70
|
+
|
|
71
|
+
## Mount the provider
|
|
72
|
+
|
|
73
|
+
Create `src/consent.tsx`. `ConsentProvider` creates the consent runtime and
|
|
74
|
+
loads the scripts. `ConsentBanner` and `ConsentDialog` render when the policy
|
|
75
|
+
asks for them, and `ConsentDialogLink` keeps preferences reachable after the
|
|
76
|
+
banner closes.
|
|
73
77
|
|
|
74
78
|
```tsx title="src/consent.tsx"
|
|
75
|
-
import type { ReactNode } from 'react';
|
|
76
79
|
import {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
ConsentBanner,
|
|
81
|
+
ConsentDialog,
|
|
82
|
+
ConsentDialogLink,
|
|
83
|
+
ConsentProvider,
|
|
84
|
+
hosted,
|
|
82
85
|
} from 'c15t/react';
|
|
83
|
-
import '
|
|
86
|
+
import type { ReactNode } from 'react';
|
|
87
|
+
|
|
84
88
|
import { scripts } from './scripts';
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const mode = hosted({ url:
|
|
89
|
-
|
|
90
|
-
export
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
import 'c15t/react/styles.css';
|
|
91
|
+
|
|
92
|
+
const mode = hosted({ url: 'https://your-project.inth.app' });
|
|
93
|
+
|
|
94
|
+
export const Consent = ({ children }: { children: ReactNode }) => (
|
|
95
|
+
<ConsentProvider options={{ mode, scripts }}>
|
|
96
|
+
{children}
|
|
97
|
+
<ConsentBanner />
|
|
98
|
+
<ConsentDialog />
|
|
99
|
+
<footer>
|
|
100
|
+
<ConsentDialogLink>Privacy settings</ConsentDialogLink>
|
|
101
|
+
</footer>
|
|
102
|
+
</ConsentProvider>
|
|
103
|
+
);
|
|
100
104
|
```
|
|
101
105
|
|
|
106
|
+
Replace `https://your-project.inth.app` with your project's backend URL,
|
|
107
|
+
exactly as Inth shows it, including any path prefix.
|
|
108
|
+
|
|
109
|
+
Wrap your app with it in `src/main.tsx`:
|
|
110
|
+
|
|
102
111
|
```tsx title="src/main.tsx"
|
|
103
112
|
import { createRoot } from 'react-dom/client';
|
|
104
|
-
|
|
113
|
+
|
|
114
|
+
import { App } from './app';
|
|
105
115
|
import { Consent } from './consent';
|
|
106
116
|
|
|
107
117
|
const root = document.getElementById('root');
|
|
108
|
-
if (!root)
|
|
109
|
-
|
|
118
|
+
if (!root) {
|
|
119
|
+
throw new Error('Missing #root element');
|
|
120
|
+
}
|
|
121
|
+
createRoot(root).render(
|
|
122
|
+
<Consent>
|
|
123
|
+
<App />
|
|
124
|
+
</Consent>
|
|
125
|
+
);
|
|
110
126
|
```
|
|
111
127
|
|
|
112
|
-
Keep `Consent` mounted
|
|
113
|
-
|
|
114
|
-
The
|
|
128
|
+
Keep `Consent` mounted for the life of the app. If you use a client-side
|
|
129
|
+
router, render the router inside `Consent` so navigation does not remount the
|
|
130
|
+
provider. The stylesheet import in `src/consent.tsx` styles the banner and
|
|
131
|
+
dialog; import it once.
|
|
132
|
+
|
|
133
|
+
## Gate your own features
|
|
115
134
|
|
|
116
|
-
|
|
135
|
+
Read a category's permission with `useConsent` inside the provider:
|
|
117
136
|
|
|
118
|
-
```tsx
|
|
137
|
+
```tsx title="src/marketing-banner.tsx"
|
|
119
138
|
import { useConsent } from 'c15t/react';
|
|
120
139
|
|
|
121
|
-
export function
|
|
122
|
-
|
|
140
|
+
export function MarketingBanner() {
|
|
141
|
+
return useConsent('marketing') ? <aside>Spring sale</aside> : null;
|
|
123
142
|
}
|
|
124
143
|
```
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
`
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
`useConsent` returns the permission right now. Under an opt-out policy it can be
|
|
146
|
+
`true` before the visitor has chosen anything. Read
|
|
147
|
+
[how consent works](../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice)
|
|
148
|
+
before you record or report choices. For iframes, use
|
|
149
|
+
[`ConsentGate`](./components/consent-gate.md).
|
|
150
|
+
|
|
151
|
+
## Check that it works
|
|
152
|
+
|
|
153
|
+
Build and preview the production bundle with `vite build` and `vite preview`,
|
|
154
|
+
then open the site in a private window with DevTools open on the Network tab.
|
|
155
|
+
Test under a policy that asks for a choice, such as an EU opt-in policy.
|
|
156
|
+
|
|
157
|
+
1. **Before a choice.** The banner shows. There are no requests to
|
|
158
|
+
`posthog.com` or `static.ads-twitter.com`.
|
|
159
|
+
2. **Reject All.** The banner closes and the vendor requests stay absent.
|
|
160
|
+
3. **Reload.** The banner stays closed and the vendor requests stay absent.
|
|
161
|
+
4. **Privacy settings.** The footer link opens the dialog with **Analytics** (the
|
|
162
|
+
`measurement` category) and **Marketing** off. Turn on **Analytics** and save. PostHog's `array.js` loads and
|
|
163
|
+
X Pixel stays absent.
|
|
164
|
+
5. **Turn Analytics off again.** The page reloads and PostHog does not load.
|
|
165
|
+
|
|
166
|
+
If the banner never appears, check the Network tab for the request to
|
|
167
|
+
`/init` on your backend URL. [Troubleshooting](./troubleshooting.md)
|
|
168
|
+
covers the common causes. [Verify consent](../../guides/verify-consent.md) has the
|
|
169
|
+
full release checklist.
|
|
170
|
+
|
|
171
|
+
## Next steps
|
|
172
|
+
|
|
173
|
+
* [Rendering](./rendering.md) for server-rendered React apps and
|
|
174
|
+
what the first paint contains.
|
|
175
|
+
* [Scripts and embeds](./scripts.md) for more vendors, network
|
|
176
|
+
blocking and data cleanup.
|
|
177
|
+
* [Customize](./customize.md) for colors, fonts, layout and copy.
|
|
178
|
+
* [Components](./components/consent-provider.md) for
|
|
179
|
+
every provider option and component prop.
|
|
180
|
+
* The runnable app in `examples/react` of the
|
|
181
|
+
[c15t repository](https://github.com/c15t/c15t) contains this setup.
|