@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.
Files changed (67) hide show
  1. package/AGENTS.md +6 -4
  2. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  3. package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
  4. package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
  5. package/dist/components/iab-panel/index.js +1 -1
  6. package/dist/components/panel/index.js +1 -1
  7. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  8. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -1
  9. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  10. package/dist/components/preferences/index.js +1 -1
  11. package/dist/components/shared/ui/accordion/accordion.js +1 -1
  12. package/dist/components/shared/ui/collapsible/collapsible.js +1 -1
  13. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  14. package/dist/components/shared/ui/switch/switch.js +1 -1
  15. package/dist/components/shared/ui/tabs/tabs.js +1 -1
  16. package/dist/devtools-launcher.js +2 -0
  17. package/dist/devtools.js +1 -1
  18. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  19. package/dist/index-parts/core.js +1 -1
  20. package/dist/provider.js +1 -1
  21. package/dist/styles.tw3.css +27 -3
  22. package/dist/version.js +1 -1
  23. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +4 -4
  24. package/dist-types/components/iab-panel/index.d.ts +0 -1
  25. package/dist-types/components/panel/index.d.ts +0 -1
  26. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +5 -0
  27. package/dist-types/components/panel-trigger/panel-trigger.d.ts +0 -8
  28. package/dist-types/components/preferences/index.d.ts +0 -1
  29. package/dist-types/components/shared/ui/accordion/accordion.d.ts +0 -1
  30. package/dist-types/components/shared/ui/collapsible/collapsible.d.ts +0 -1
  31. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +0 -1
  32. package/dist-types/components/shared/ui/switch/switch.d.ts +0 -1
  33. package/dist-types/components/shared/ui/tabs/tabs.d.ts +0 -1
  34. package/dist-types/devtools-launcher.d.ts +29 -0
  35. package/dist-types/hooks/use-iab-manager.d.ts +4 -4
  36. package/dist-types/hooks.d.ts +27 -5
  37. package/dist-types/index-parts/core.d.ts +1 -1
  38. package/dist-types/index.d.ts +1 -1
  39. package/dist-types/provider.d.ts +12 -1
  40. package/dist-types/version.d.ts +1 -1
  41. package/docs/README.md +6 -4
  42. package/docs/concepts/choose-your-setup.md +22 -5
  43. package/docs/concepts/consent-state.md +41 -0
  44. package/docs/concepts/data-fetching.md +34 -11
  45. package/docs/customization/recipes.md +4 -5
  46. package/docs/customization/slots.md +3 -2
  47. package/docs/customization/stylesheets.md +29 -39
  48. package/docs/customization/tailwind.md +5 -5
  49. package/docs/customization/tokens.md +1 -1
  50. package/docs/customization/translations.md +32 -5
  51. package/docs/frameworks/react/components/consent-dialog-trigger.md +5 -0
  52. package/docs/frameworks/react/components/consent-dialog.md +2 -1
  53. package/docs/frameworks/react/components/dev-tools.md +23 -0
  54. package/docs/frameworks/react/headless.md +4 -4
  55. package/docs/frameworks/react/hooks.md +10 -5
  56. package/docs/frameworks/react/iab.md +24 -18
  57. package/docs/frameworks/react/quickstart.md +77 -16
  58. package/docs/frameworks/react/rendering.md +82 -11
  59. package/docs/frameworks/react/scripts.md +8 -3
  60. package/docs/frameworks/react/upgrade-v3.md +596 -0
  61. package/docs/frameworks/react/vendor-consent.md +6 -0
  62. package/docs/guides/banner-experiments.md +26 -5
  63. package/docs/guides/troubleshooting.md +3 -2
  64. package/docs/integrations/overview.md +28 -27
  65. package/docs/integrations/sentry.md +416 -0
  66. package/docs/upgrade-v3.md +201 -314
  67. package/package.json +8 -8
@@ -8,8 +8,9 @@ group: frameworks
8
8
  ## Before you start
9
9
 
10
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.
11
+ loads the page, resolves the visitor's policy, then shows the banner or runs
12
+ the scripts the visitor allowed. The build-time step bundles the public
13
+ policy; regional policies may still need a backend location request.
13
14
 
14
15
  * **Next.js or TanStack Start?** Use that framework's guide from the
15
16
  [framework list](https://c15t.com/docs/frameworks). Their adapters resolve consent on the
@@ -26,6 +27,10 @@ the backend URL.
26
27
  [Choose your setup](../../concepts/choose-your-setup.md#decide-who-runs-the-consent-backend)
27
28
  covers self-hosting and offline mode.
28
29
 
30
+ Build-time manifests are the recommended production setup. Complete the
31
+ [build-time manifest step](#bundle-the-manifest-during-builds) before
32
+ deploying, and rebuild after changing policies, translations or vendors.
33
+
29
34
  ## Install c15t
30
35
 
31
36
  | Package manager | Command |
@@ -40,14 +45,12 @@ vendor loaders used below.
40
45
 
41
46
  ## Register your vendor scripts
42
47
 
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.
48
+ Create `src/scripts.ts`. PostHog waits for measurement permission, so your
49
+ policy must include that category. Remove any other PostHog loader from your
50
+ app so PostHog loads once.
47
51
 
48
52
  ```ts title="src/scripts.ts"
49
53
  import { posthog } from '@c15t/integrations/posthog';
50
- import { xPixel } from '@c15t/integrations/x-pixel';
51
54
 
52
55
  export const scripts = [
53
56
  posthog({
@@ -55,13 +58,11 @@ export const scripts = [
55
58
  initOptions: { cookieless_mode: 'never' },
56
59
  loadMode: 'after-consent',
57
60
  }),
58
- xPixel({ pixelId: 'your-pixel-id' }),
59
61
  ];
60
62
  ```
61
63
 
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.
64
+ Replace `phc_your_project_key` with your PostHog project key. PostHog uses its
65
+ EU host; add `region: 'us'` for a US project.
65
66
 
66
67
  `loadMode: 'after-consent'` keeps the PostHog SDK off the page until the
67
68
  visitor allows measurement. `cookieless_mode: 'never'` turns off PostHog's
@@ -130,6 +131,66 @@ router, render the router inside `Consent` so navigation does not remount the
130
131
  provider. The stylesheet import in `src/consent.tsx` styles the banner and
131
132
  dialog; import it once.
132
133
 
134
+ ## Bundle the manifest during builds
135
+
136
+ The build fetches your public policy once and bundles it, so the server never
137
+ fetches it at runtime.
138
+
139
+ Add `consentManifest` to your existing Vite plugin list:
140
+
141
+ ```ts title="vite.config.ts (plugins option)"
142
+ import { consentManifest } from 'c15t/build';
143
+
144
+ export default {
145
+ plugins: [
146
+ // Keep your existing framework plugins here.
147
+ consentManifest({ backendURL: 'https://your-project.inth.app' }),
148
+ ],
149
+ };
150
+ ```
151
+
152
+ Vite writes `src/c15t-manifest.ts` before compiling, including at development
153
+ startup. Keep the generated file out of Git. Install `@c15t/browser@alpha` for
154
+ its browser manifest transport. In the quickstart's `src/consent.tsx`, replace
155
+ the `hosted()` mode with this factory:
156
+
157
+ ```tsx title="src/consent.tsx"
158
+ import { manifest } from '@c15t/browser/headless';
159
+
160
+ import { consentManifest } from './c15t-manifest';
161
+
162
+ const mode = manifest({
163
+ backendURL: 'https://your-project.inth.app',
164
+ manifest: consentManifest,
165
+ });
166
+ ```
167
+
168
+ Keep passing `mode` to `ConsentProvider`. The browser resolves the snapshot
169
+ without `/manifest` when it has the geography the policy needs, or the policy
170
+ does not depend on geography. Supply known country and region through `inputs`
171
+ or overrides to keep regional resolution on the snapshot.
172
+
173
+ When the required country or region is missing, the transport calls backend
174
+ `/init` and uses its resolved policy and translations. That response can use
175
+ newer backend policy than the build snapshot. This is a full consent resolution,
176
+ so these visitors still wait for a backend round trip.
177
+
178
+ Policy and resolver code are part of the browser bundle.
179
+
180
+ The snapshot is fixed at build time:
181
+
182
+ * Rebuild after changing policies, translations or vendors. If your CI caches
183
+ build output, force a fresh build.
184
+ * The build must reach the backend. A failed fetch fails the build.
185
+ * Consent choices still go to the backend.
186
+
187
+ Run `vite build` or `vite dev` before a standalone `tsc`, `vue-tsc` or
188
+ `svelte-check`; the generated file doesn't exist on a fresh checkout.
189
+
190
+ For policy updates without a rebuild, keep the original `hosted()` factory
191
+ and omit the build plugin. That setup resolves policy through the backend
192
+ `/init` on each page load.
193
+
133
194
  ## Gate your own features
134
195
 
135
196
  Read a category's permission with `useConsent` inside the provider:
@@ -155,16 +216,16 @@ then open the site in a private window with DevTools open on the Network tab.
155
216
  Test under a policy that asks for a choice, such as an EU opt-in policy.
156
217
 
157
218
  1. **Before a choice.** The banner shows. There are no requests to
158
- `posthog.com` or `static.ads-twitter.com`.
219
+ `posthog.com`.
159
220
  2. **Reject All.** The banner closes and the vendor requests stay absent.
160
221
  3. **Reload.** The banner stays closed and the vendor requests stay absent.
161
222
  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.
223
+ `measurement` category) and **Marketing** off. Turn on **Analytics** and
224
+ save. PostHog's `array.js` loads.
164
225
  5. **Turn Analytics off again.** The page reloads and PostHog does not load.
165
226
 
166
- If the banner never appears, check the Network tab for the request to
167
- `/init` on your backend URL. [Troubleshooting](./troubleshooting.md)
227
+ If the banner never appears, check the generated policy and any location
228
+ request to `/init` on your backend URL. [Troubleshooting](./troubleshooting.md)
168
229
  covers the common causes. [Verify consent](../../guides/verify-consent.md) has the
169
230
  full release checklist.
170
231
 
@@ -23,14 +23,79 @@ read the visitor's cookie and location on the server, so the banner can be in
23
23
  the first HTML. [Choose your setup](../../concepts/choose-your-setup.md#server-rendering-or-browser-rendering)
24
24
  explains the difference in general terms.
25
25
 
26
+ ## Bundle the manifest during Vite builds
27
+
28
+ The build fetches your public policy once and bundles it, so the server never
29
+ fetches it at runtime.
30
+
31
+ Add `consentManifest` to your existing Vite plugin list:
32
+
33
+ ```ts title="vite.config.ts (plugins option)"
34
+ import { consentManifest } from 'c15t/build';
35
+
36
+ export default {
37
+ plugins: [
38
+ // Keep your existing framework plugins here.
39
+ consentManifest({ backendURL: 'https://your-project.inth.app' }),
40
+ ],
41
+ };
42
+ ```
43
+
44
+ Vite writes `src/c15t-manifest.ts` before compiling, including at development
45
+ startup. Keep the generated file out of Git. Install `@c15t/browser@alpha` for
46
+ its browser manifest transport. In the quickstart's `src/consent.tsx`, replace
47
+ the `hosted()` mode with this factory:
48
+
49
+ ```tsx title="src/consent.tsx"
50
+ import { manifest } from '@c15t/browser/headless';
51
+
52
+ import { consentManifest } from './c15t-manifest';
53
+
54
+ const mode = manifest({
55
+ backendURL: 'https://your-project.inth.app',
56
+ manifest: consentManifest,
57
+ });
58
+ ```
59
+
60
+ Keep passing `mode` to `ConsentProvider`. The browser resolves the snapshot
61
+ without `/manifest` when it has the geography the policy needs, or the policy
62
+ does not depend on geography. Supply known country and region through `inputs`
63
+ or overrides to keep regional resolution on the snapshot.
64
+
65
+ When the required country or region is missing, the transport calls backend
66
+ `/init` and uses its resolved policy and translations. That response can use
67
+ newer backend policy than the build snapshot. This is a full consent resolution,
68
+ so these visitors still wait for a backend round trip.
69
+
70
+ Policy and resolver code are part of the browser bundle.
71
+
72
+ The snapshot is fixed at build time:
73
+
74
+ * Rebuild after changing policies, translations or vendors. If your CI caches
75
+ build output, force a fresh build.
76
+ * The build must reach the backend. A failed fetch fails the build.
77
+ * Consent choices still go to the backend.
78
+
79
+ Run `vite build` or `vite dev` before a standalone `tsc`, `vue-tsc` or
80
+ `svelte-check`; the generated file doesn't exist on a fresh checkout.
81
+
82
+ For policy updates without a rebuild, keep the original `hosted()` factory
83
+ and omit the build plugin. That setup resolves policy through the backend
84
+ `/init` on each page load.
85
+
26
86
  ## Client-rendered apps
27
87
 
28
- The [quickstart](./quickstart.md) setup runs entirely in the
29
- browser:
88
+ The recommended [quickstart](./quickstart.md) bundles the
89
+ manifest and resolves it in the browser. See [build-time manifests](#bundle-the-manifest-during-vite-builds).
90
+
91
+ For policy updates without rebuilding, keep `hosted({ url })`. That runtime
92
+ path works as follows:
30
93
 
31
94
  1. The page loads with no consent state. Every optional category is denied.
32
- 2. `ConsentProvider` requests `GET /init` from your backend URL. The backend
33
- picks the visitor's policy from their location.
95
+ 2. `ConsentProvider` requests `GET /init` from your backend URL while it first
96
+ renders, before the page paints. The backend picks the visitor's policy
97
+ from their location. With an `experiment`, the request waits until the
98
+ provider mounts, because it carries the visitor's arm.
34
99
  3. The provider applies the policy and any stored choice. It shows the banner
35
100
  if the policy asks for one, and loads the scripts the visitor allowed.
36
101
 
@@ -47,8 +112,11 @@ around the outlet. In React Router framework mode that is `app/root.tsx`.
47
112
  browser takes over after hydration:
48
113
 
49
114
  * The server HTML contains your page but no banner and no consent state.
50
- * After hydration the browser requests `/init`, then shows the banner if the
51
- policy asks for one.
115
+ * After hydration the browser resolves the bundled manifest, then shows the
116
+ banner if the policy asks for one. When required country or region is
117
+ missing, it calls `/init` and uses the backend's current policy and
118
+ translations, which can differ from the build snapshot. With `hosted()`,
119
+ it always calls `/init`.
52
120
  * The server render and the first browser render agree, so hydration does not
53
121
  warn or change a stored choice.
54
122
 
@@ -96,8 +164,9 @@ browser path.
96
164
  ## Static and prerendered pages
97
165
 
98
166
  A prerendered page is the same HTML for every visitor, so it cannot contain one
99
- visitor's consent. Use the quickstart setup unchanged. The browser talks to the
100
- backend URL directly, so the static host needs no server route. Add the site's
167
+ visitor's consent. Use the quickstart's build-time snapshot. Consent saves and
168
+ any `/init` requests go to the backend URL directly, so the static host
169
+ needs no server route. Add the site's
101
170
  origin to the trusted origins in your Inth project.
102
171
 
103
172
  Do not cache a response that contains a visitor's consent in a CDN or a shared
@@ -122,7 +191,9 @@ custom transports.
122
191
  1. Open the page in a private window and view the page source. In a
123
192
  server-rendered app the HTML contains your content and no element with
124
193
  `data-testid="consent-banner-root"`.
125
- 2. In DevTools Network, filter by your backend URL. One `/init` request runs
126
- after the JavaScript loads, or none if you pass `initialData`.
127
- 3. The banner appears once that request finishes. Vendor requests stay absent
194
+ 2. In DevTools Network, confirm there is no `/manifest` request with a
195
+ build-time snapshot. A regional policy may still need an `/init` request
196
+ to resolve consent when geography is missing. With `hosted()`, one `/init`
197
+ request runs unless you supply `initialData`.
198
+ 3. The banner appears once the policy resolves. Vendor requests stay absent
128
199
  until you allow their category.
@@ -11,11 +11,10 @@ group: frameworks
11
11
  Pass vendor loaders to `ConsentProvider` through `options.scripts`. The
12
12
  provider loads each script when its category is allowed and removes it when
13
13
  the category is denied. The [quickstart](./quickstart.md)
14
- registers PostHog and X Pixel this way:
14
+ registers PostHog this way:
15
15
 
16
16
  ```ts title="src/scripts.ts"
17
17
  import { posthog } from '@c15t/integrations/posthog';
18
- import { xPixel } from '@c15t/integrations/x-pixel';
19
18
 
20
19
  export const scripts = [
21
20
  posthog({
@@ -23,7 +22,6 @@ export const scripts = [
23
22
  initOptions: { cookieless_mode: 'never' },
24
23
  loadMode: 'after-consent',
25
24
  }),
26
- xPixel({ pixelId: 'your-pixel-id' }),
27
25
  ];
28
26
  ```
29
27
 
@@ -76,6 +74,13 @@ covers a vendor without one.
76
74
  load before consent and pass the visitor's choice to the vendor. Read the
77
75
  vendor's guide; a category on a script does not always mean zero requests.
78
76
 
77
+ ## When the script loader downloads
78
+
79
+ The script loader is a separate chunk, downloaded only when `scripts` is not
80
+ empty. `ConsentProvider` loads it when it mounts. The Next.js and TanStack
81
+ Start `ConsentRoot` start the download earlier, during their first render,
82
+ when the visitor's consent already lets a script run.
83
+
79
84
  ## Embeds and other requests
80
85
 
81
86
  Scripts cover vendor code c15t loads for you. For the rest: