@c15t/react 3.0.0-alpha.8 → 3.0.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.
Files changed (164) hide show
  1. package/AGENTS.md +10 -12
  2. package/README.md +41 -70
  3. package/SKILL.md +1 -1
  4. package/dist/aggregate-components.js +1 -1
  5. package/dist/components/consent-gate/atoms.js +1 -1
  6. package/dist/components/consent-gate/frame.js +2 -0
  7. package/dist/components/consent-gate/index.js +1 -1
  8. package/dist/components/iab-panel/atoms/root.js +1 -1
  9. package/dist/components/iab-panel/iab-panel.js +1 -1
  10. package/dist/components/iab-prompt/atoms/root.js +1 -1
  11. package/dist/components/iab-prompt/iab-prompt.js +1 -1
  12. package/dist/components/panel/atoms/root.js +1 -1
  13. package/dist/components/panel-trigger/atoms/root.js +1 -1
  14. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  15. package/dist/components/panel-trigger/toolbar-loader.js +2 -0
  16. package/dist/components/preferences/atoms/root.js +1 -1
  17. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  18. package/dist/components/prompt/atoms/root.js +1 -1
  19. package/dist/components/prompt/prompt.js +1 -1
  20. package/dist/components/shared/dialog-sheets.js +1 -0
  21. package/dist/components/shared/first-paint-sheets.js +1 -0
  22. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  23. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  24. package/dist/components/shared/primitives/button.js +1 -1
  25. package/dist/components/shared/surface-styles.js +2 -0
  26. package/dist/consent-gate.js +1 -1
  27. package/dist/context/index.js +1 -1
  28. package/dist/devtools.js +1 -1
  29. package/dist/hooks/use-iab-policy-guard.js +1 -0
  30. package/dist/iab/styles.tw3.css +2 -2
  31. package/dist/iab-context.js +1 -1
  32. package/dist/iab-dialog-deferred.js +2 -0
  33. package/dist/iab.js +1 -1
  34. package/dist/index-parts/consent-gate.js +1 -1
  35. package/dist/index-parts/core.js +1 -1
  36. package/dist/index-parts/frame.js +1 -0
  37. package/dist/index-parts/hosted.js +1 -0
  38. package/dist/index-parts/offline.js +1 -0
  39. package/dist/index-parts/presets.js +1 -0
  40. package/dist/index.js +1 -1
  41. package/dist/modes.js +1 -0
  42. package/dist/panel-link.js +1 -1
  43. package/dist/panel-trigger.js +1 -1
  44. package/dist/provider.js +1 -1
  45. package/dist/styles.tw3.css +2 -2
  46. package/dist/transports/hosted.js +1 -0
  47. package/dist/transports/manifest.js +1 -0
  48. package/dist/utils/deferred-module.js +1 -1
  49. package/dist/version.js +1 -1
  50. package/dist-types/chunk-warming.d.ts +7 -6
  51. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  52. package/dist-types/components/consent-gate/index.d.ts +3 -19
  53. package/dist-types/components/iab-prompt/iab-prompt.d.ts +1 -1
  54. package/dist-types/components/panel/index.d.ts +1 -1
  55. package/dist-types/components/panel-trigger/toolbar-loader.d.ts +34 -0
  56. package/dist-types/components/preferences/index.d.ts +1 -1
  57. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  58. package/dist-types/components/prompt/index.d.ts +1 -1
  59. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  60. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  61. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  62. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  63. package/dist-types/components/shared/surface-styles.d.ts +60 -0
  64. package/dist-types/consent-gate.d.ts +1 -1
  65. package/dist-types/context/index.d.ts +3 -3
  66. package/dist-types/context.d.ts +2 -0
  67. package/dist-types/gpp.d.ts +1 -1
  68. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  69. package/dist-types/iab-dialog-deferred.d.ts +36 -0
  70. package/dist-types/iab.d.ts +3 -2
  71. package/dist-types/index-parts/consent-gate.d.ts +1 -1
  72. package/dist-types/index-parts/core.d.ts +1 -2
  73. package/dist-types/index-parts/frame.d.ts +7 -0
  74. package/dist-types/index-parts/hosted.d.ts +7 -0
  75. package/dist-types/index-parts/offline.d.ts +7 -0
  76. package/dist-types/index-parts/presets.d.ts +7 -0
  77. package/dist-types/index.d.ts +5 -1
  78. package/dist-types/modes.d.ts +26 -0
  79. package/dist-types/panel-link.d.ts +1 -1
  80. package/dist-types/panel-trigger.d.ts +1 -1
  81. package/dist-types/provider.d.ts +22 -4
  82. package/dist-types/transports/hosted.d.ts +36 -0
  83. package/dist-types/transports/manifest.d.ts +48 -0
  84. package/dist-types/ui-config-context.d.ts +4 -0
  85. package/dist-types/version.d.ts +1 -1
  86. package/docs/README.md +10 -12
  87. package/docs/concepts/choose-your-setup.md +45 -8
  88. package/docs/concepts/consent-categories.md +1 -1
  89. package/docs/concepts/consent-state.md +5 -2
  90. package/docs/concepts/data-fetching.md +100 -40
  91. package/docs/concepts/modes.md +281 -0
  92. package/docs/customization/dark-mode.md +1 -1
  93. package/docs/customization/motion.md +2 -2
  94. package/docs/customization/overview.md +3 -2
  95. package/docs/customization/recipes.md +4 -4
  96. package/docs/customization/slots.md +25 -25
  97. package/docs/customization/stylesheets.md +161 -50
  98. package/docs/customization/tailwind.md +71 -50
  99. package/docs/customization/tokens.md +42 -13
  100. package/docs/frameworks/react/components/consent-banner.md +14 -12
  101. package/docs/frameworks/react/components/consent-dialog.md +15 -13
  102. package/docs/frameworks/react/components/consent-provider.md +51 -46
  103. package/docs/frameworks/react/components.md +16 -13
  104. package/docs/frameworks/react/compose.md +3 -3
  105. package/docs/frameworks/react/content-security-policy.md +35 -12
  106. package/docs/frameworks/react/customize.md +21 -13
  107. package/docs/frameworks/react/hooks.md +2 -2
  108. package/docs/frameworks/react/iab.md +56 -6
  109. package/docs/frameworks/react/quickstart.md +65 -99
  110. package/docs/frameworks/react/rendering.md +170 -94
  111. package/docs/frameworks/react/scripts.md +19 -30
  112. package/docs/frameworks/react/troubleshooting.md +100 -32
  113. package/docs/frameworks/react/upgrade-v3.md +133 -79
  114. package/docs/frameworks/react/vendor-consent.md +19 -7
  115. package/docs/guides/banner-experiments.md +21 -8
  116. package/docs/guides/troubleshooting.md +90 -26
  117. package/docs/guides/verify-consent.md +23 -8
  118. package/docs/integrations/adobe-analytics.md +60 -64
  119. package/docs/integrations/ahrefs-analytics.md +58 -63
  120. package/docs/integrations/amplitude.md +60 -64
  121. package/docs/integrations/clearbit.md +59 -63
  122. package/docs/integrations/cloudflare-web-analytics.md +56 -60
  123. package/docs/integrations/cloudflare-zaraz.md +55 -57
  124. package/docs/integrations/crisp.md +64 -69
  125. package/docs/integrations/databuddy.md +62 -64
  126. package/docs/integrations/fathom-analytics.md +56 -60
  127. package/docs/integrations/front-chat.md +56 -60
  128. package/docs/integrations/google-maps.md +25 -13
  129. package/docs/integrations/google-tag-manager.md +153 -77
  130. package/docs/integrations/google-tag.md +134 -78
  131. package/docs/integrations/heap.md +56 -60
  132. package/docs/integrations/hightouch.md +55 -60
  133. package/docs/integrations/hotjar.md +60 -64
  134. package/docs/integrations/intercom.md +55 -60
  135. package/docs/integrations/klaviyo.md +65 -66
  136. package/docs/integrations/linkedin-insights.md +58 -63
  137. package/docs/integrations/logrocket.md +55 -60
  138. package/docs/integrations/matomo-analytics.md +58 -60
  139. package/docs/integrations/meta-pixel.md +56 -60
  140. package/docs/integrations/microsoft-clarity.md +57 -60
  141. package/docs/integrations/microsoft-uet.md +58 -61
  142. package/docs/integrations/mixpanel-analytics.md +73 -75
  143. package/docs/integrations/one-dollar-stats.md +53 -59
  144. package/docs/integrations/openai-pixel.md +58 -60
  145. package/docs/integrations/overview.md +59 -42
  146. package/docs/integrations/pinterest-tag.md +56 -60
  147. package/docs/integrations/pirsch.md +54 -60
  148. package/docs/integrations/plausible-analytics.md +55 -60
  149. package/docs/integrations/posthog.md +56 -60
  150. package/docs/integrations/promptwatch.md +57 -63
  151. package/docs/integrations/reddit-pixel.md +57 -60
  152. package/docs/integrations/rudderstack.md +65 -67
  153. package/docs/integrations/rybbit-analytics.md +70 -75
  154. package/docs/integrations/segment.md +56 -60
  155. package/docs/integrations/sentry.md +65 -60
  156. package/docs/integrations/snapchat-pixel.md +54 -60
  157. package/docs/integrations/tiktok-pixel.md +59 -63
  158. package/docs/integrations/umami-analytics.md +54 -60
  159. package/docs/integrations/vercel-analytics.md +53 -59
  160. package/docs/integrations/vercel-speed-insights.md +317 -0
  161. package/docs/integrations/x-pixel.md +57 -63
  162. package/docs/integrations/youtube.md +25 -13
  163. package/package.json +18 -8
  164. package/docs/upgrade-v3.md +0 -429
package/docs/README.md CHANGED
@@ -6,7 +6,7 @@ These docs ship inside the package so coding agents can read them offline. Open
6
6
 
7
7
  ## Using these docs
8
8
 
9
- These docs describe c15t v3. Find your app's row in Choose your setup, then follow that framework guide from start to finish; it names the files to create and the backend URL to use. Install c15t packages with the @alpha dist-tag, because npm latest is still v2. A visible banner does not prove anything: check that vendor requests wait for consent, that rejection survives a reload, and that preferences can be reopened.
9
+ These docs describe c15t v3. Find your app's row in Choose your setup, then follow that framework guide from start to finish; it names the files to create and the backend URL to use. A visible banner does not prove anything: check that vendor requests wait for consent, that rejection survives a reload, and that preferences can be reopened.
10
10
 
11
11
  ## Start here
12
12
 
@@ -15,7 +15,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
15
15
  - [How consent works](./concepts/how-consent-works.md): What c15t decides on each page load, the difference between a permission and a recorded choice, and what happens when a visitor saves.
16
16
  - [Customize the interface](./customization/overview.md): Change c15t's consent banner and dialog one step at a time, from a prop to your own markup, and find where each step lives in your framework.
17
17
  - [Verify consent](./guides/verify-consent.md): Check in a production build that vendor requests wait for consent, rejection survives a reload, preferences reopen and privacy signals apply.
18
- - [Migrate to v3](./upgrade-v3.md): Upgrade from c15t v2 to v3. Pick the guide for the v2 package you use (@c15t/nextjs, @c15t/react or the c15t store), rename @c15t/scripts, keep visitors' choices, and upgrade a self-hosted backend and the Node.js SDK.
18
+ - [Upgrade a React app from v2](./frameworks/react/upgrade-v3.md): Upgrade a React app from c15t v2 (@c15t/react) to v3. Covers packages, ConsentProvider and transports, the useConsentManager codemod, callbacks, policies, styles, IAB and stored consent.
19
19
 
20
20
  ## More documentation
21
21
 
@@ -36,7 +36,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
36
36
  - [DevTools](./frameworks/react/components/dev-tools.md): Inspect consent state, location, loaded scripts and consent events in a React app during development with the c15t DevTools panel.
37
37
  - [Compose your own banner](./frameworks/react/compose.md): Build a React consent banner from the ConsentBanner parts in c15t/react, with your own markup and buttons, while the policy still decides which actions appear.
38
38
  - [Content Security Policy](./frameworks/react/content-security-policy.md): Run c15t in a React app under a Content Security Policy, allow the consent backend and vendor hosts, and pass a nonce to ConsentProvider and ConsentTheme.
39
- - [Customize](./frameworks/react/customize.md): Change the colors, fonts, layout, button styles and copy of the c15t banner and dialog in a React app with the stylesheet, theme tokens, provider options and slots.
39
+ - [Customize](./frameworks/react/customize.md): Change the colors, fonts, layout, button styles and copy of the c15t banner and dialog in a React app with theme tokens, provider options and slots, and load the stylesheet yourself when you need to.
40
40
  - [Embeds](./frameworks/react/embeds.md): Keep YouTube videos, maps and other iframes out of a React page until their consent category is allowed, with ConsentGate or the iframe blocker in ConsentProvider.
41
41
  - [IAB GPP](./frameworks/react/gpp.md): Add the IAB Global Privacy Platform API (__gpp) to a React app with ConsentGPP from @c15t/react/gpp, so ad tech can read US state opt-outs and the TCF EU consent string.
42
42
  - [Headless](./frameworks/react/headless.md): Build a custom consent banner in React with the c15t/react/headless hooks inside your ConsentProvider.
@@ -58,12 +58,13 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
58
58
  - [Consent state reference](./concepts/consent-state.md): How c15t saves choices, gates IAB vendors, hydrates server records and keeps browser tabs and storage in step.
59
59
  - [Data fetching](./concepts/data-fetching.md): Choose the recommended build-time policy snapshot or runtime manifest fetching, backend /init, browser resolution or offline rules, and understand where consent choices are saved.
60
60
  - [How consent works](./concepts/how-consent-works.md): What c15t decides on each page load, the difference between a permission and a recorded choice, and what happens when a visitor saves.
61
+ - [Consent modes](./concepts/modes.md): Reference for manifest(), hosted() and offline() in every c15t framework package, where each one resolves the visitor's policy, what it adds to first-load JavaScript, the build-time manifest fetch and its failure policy, and the backend URL variable each framework reads.
61
62
  - [Policies](./concepts/policies.md): How policy models, prompts and scope decide what c15t asks visitors, where to change the rules, and why a banner may not appear.
62
63
 
63
64
  ## Guides
64
65
 
65
66
  - [Banner experiments](./guides/banner-experiments.md): Run A/B tests on consent banner presentation with any feature-flag provider or built-in weighted assignment, and attribute every impression and choice to its arm.
66
- - [Troubleshooting](./guides/troubleshooting.md): Fix a missing banner, analytics that load before consent, choices lost on reload, CORS errors, hydration differences and failed static builds in c15t v3.
67
+ - [Troubleshooting](./guides/troubleshooting.md): Fix a missing banner, analytics that load before consent, choices lost on reload, CORS errors, hydration differences, failed manifest downloads, mode errors and failed static builds in c15t v3.
67
68
  - [Verify consent](./guides/verify-consent.md): Check in a production build that vendor requests wait for consent, rejection survives a reload, preferences reopen and privacy signals apply.
68
69
 
69
70
  ## Customization
@@ -74,8 +75,8 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
74
75
  - [Customize the interface](./customization/overview.md): Change c15t's consent banner and dialog one step at a time, from a prop to your own markup, and find where each step lives in your framework.
75
76
  - [Banner designs](./customization/recipes.md): Five consent banner designs built with c15t, from one prop to your own markup, with tested code for React, Vue, Svelte, Astro and plain HTML.
76
77
  - [Component parts](./customization/slots.md): Find every part of c15t's banner, dialog, widget, trigger and ConsentGate placeholder, its key in your framework's part API, and the data attributes to select on.
77
- - [Stylesheets and CSS layers](./customization/stylesheets.md): Load the right c15t stylesheet for your framework, see which file holds the dialog's rules, order c15t's cascade layer against your own, and run c15t without its styles.
78
- - [Tailwind CSS](./customization/tailwind.md): Load c15t's styles next to Tailwind CSS 4 or 3 in every framework, put utilities on c15t component parts, and use Tailwind's dark variant with c15t.
78
+ - [Stylesheets and CSS layers](./customization/stylesheets.md): See how c15t delivers its styles in each framework without a render-blocking stylesheet, when to import styles.css yourself with styles false, which file holds the dialog's rules, and how c15t's cascade layer meets your own.
79
+ - [Tailwind CSS](./customization/tailwind.md): Use c15t's styles with Tailwind CSS 4 or 3 in every framework, put utilities on c15t component parts, and use Tailwind's dark variant with c15t.
79
80
  - [Theme tokens](./customization/tokens.md): Change c15t's colors, type, radius, spacing, shadows and motion with theme tokens, and see every --c15t-* variable with its default.
80
81
  - [Copy and translations](./customization/translations.md): Change consent wording through i18n and test the complete prompt and preferences flow.
81
82
 
@@ -93,8 +94,8 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
93
94
  - [Fathom Analytics](./integrations/fathom-analytics.md): Load Fathom Analytics only after measurement consent with the c15t fathomAnalytics helper, set its SPA mode, and check it in DevTools.
94
95
  - [Front Chat](./integrations/front-chat.md): Load the Front Chat widget only after functionality consent with the c15t frontChat helper, forward CSP nonces, clear the session on revocation and check it in DevTools.
95
96
  - [Google Maps](./integrations/google-maps.md): Gate a Google Maps iframe embed with c15t v3 so the map loads only after the visitor allows its consent category, in Next.js, TanStack Start, React, Nuxt, Vue, Astro, Svelte, SvelteKit, HTML or JavaScript.
96
- - [Google Tag](./integrations/google-tag.md): Load gtag.js for Google Analytics or Google Ads with c15t Consent Mode v2 signals, and verify the consent commands in DevTools.
97
- - [Google Tag Manager](./integrations/google-tag-manager.md): Load a Google Tag Manager container with c15t Consent Mode v2 signals, configure consent checks inside the container, and verify both in DevTools.
97
+ - [Google Tag](./integrations/google-tag.md): Load gtag.js for Google Analytics or Google Ads before or after consent with c15t Consent Mode v2 signals, and verify the consent commands in DevTools.
98
+ - [Google Tag Manager](./integrations/google-tag-manager.md): Load a Google Tag Manager container before or after consent with c15t Consent Mode v2 signals, configure consent checks inside the container, and verify both in DevTools.
98
99
  - [Heap](./integrations/heap.md): Load the Heap config script and heap.js only after measurement consent with the c15t heap helper, and check it in DevTools.
99
100
  - [Hightouch](./integrations/hightouch.md): Load the Hightouch Events browser SDK only after measurement consent with the c15t hightouch helper, and check page events in DevTools.
100
101
  - [Hotjar](./integrations/hotjar.md): Load Hotjar only after measurement consent with the c15t hotjar helper, and check its loader and recordings in DevTools.
@@ -124,9 +125,6 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
124
125
  - [TikTok Pixel](./integrations/tiktok-pixel.md): Load the TikTok Pixel only after marketing consent with the c15t tiktokPixel helper, guard ttq event calls, and check it in DevTools.
125
126
  - [Umami Analytics](./integrations/umami-analytics.md): Load the Umami Analytics tracker only after measurement consent with the c15t umamiAnalytics helper, point it at a self-hosted instance, and check it in DevTools.
126
127
  - [Vercel Analytics](./integrations/vercel-analytics.md): Load the Vercel Web Analytics script only after measurement consent with the c15t vercelAnalytics helper, choose the debug script, and check it in DevTools.
128
+ - [Vercel Speed Insights](./integrations/vercel-speed-insights.md): Load the Vercel Speed Insights collector only after measurement consent with the c15t vercelSpeedInsights helper, drop Web Vitals reports on revocation, and check it in DevTools.
127
129
  - [X Pixel](./integrations/x-pixel.md): Load the X Pixel only after marketing consent with the c15t xPixel helper, guard twq conversion events, and check it in DevTools.
128
130
  - [YouTube](./integrations/youtube.md): Gate YouTube embeds with c15t v3 in Next.js, TanStack Start, React, Nuxt, Vue, Astro, Svelte, SvelteKit or JavaScript.
129
-
130
- ## Reference
131
-
132
- - [Migrate to v3](./upgrade-v3.md): Upgrade from c15t v2 to v3. Pick the guide for the v2 package you use (@c15t/nextjs, @c15t/react or the c15t store), rename @c15t/scripts, keep visitors' choices, and upgrade a self-hosted backend and the Node.js SDK.
@@ -76,10 +76,15 @@ public policy manifest, never a response that contains a visitor's consent.
76
76
  ## How the server gets the policy
77
77
 
78
78
  Use a build-time manifest as the default for supported production builds.
79
- Next.js, TanStack Start, Nuxt server output, Astro server output and SvelteKit
80
- can resolve requests from the deployment's snapshot. Vite apps can bundle it
81
- for browser resolution too; location-based policies may still need a location
82
- request. The framework quickstarts show the build hook and snapshot wiring.
79
+ `manifest()` is the default mode in Next.js, TanStack Start, Nuxt, Astro and
80
+ SvelteKit, so their servers resolve requests from the deployment's snapshot.
81
+ React, Vue and Svelte single-page apps have no server, so their quickstarts
82
+ use `hosted()`: the backend reads each visitor's location from the `/init`
83
+ request. They can bundle the manifest for browser resolution too, but a
84
+ regional policy still needs `/init` on a first visit unless the page knows
85
+ the location. The framework quickstarts show the build
86
+ integration. [Consent modes](./modes.md) lists every mode, its
87
+ options and the backend URL variable each framework reads.
83
88
 
84
89
  The build fetches your public policy once and bundles it, so the server never
85
90
  fetches it at runtime.
@@ -88,17 +93,49 @@ The snapshot is fixed at build time:
88
93
 
89
94
  * Rebuild after changing policies, translations or vendors. If your CI caches
90
95
  build output, force a fresh build.
91
- * The build must reach the backend. A failed fetch fails the build.
92
96
  * Consent choices still go to the backend.
93
97
 
98
+ The build reads the backend URL from your public backend URL variable when
99
+ the config doesn't pass one: `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js,
100
+ `NUXT_PUBLIC_C15T_BACKEND_URL` in Nuxt, `PUBLIC_C15T_BACKEND_URL` in Astro,
101
+ Svelte and SvelteKit, and `VITE_C15T_BACKEND_URL` in TanStack Start and other
102
+ Vite apps. Each also reads the matching Inth variable, such as
103
+ `NEXT_PUBLIC_INTH_PROJECT_URL`, when the c15t one is unset. See
104
+ [set the backend URL](./modes.md#set-the-backend-url).
105
+
106
+ The fetch waits at most 10 seconds. When it fails, or no backend URL is set,
107
+ every framework does the same thing:
108
+
109
+ | Command | Default when the fetch fails |
110
+ | ------------------------------------------------------------------------- | -------------------------------------------------------- |
111
+ | Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
112
+ | Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning, and the server fetches the policy at runtime. |
113
+
114
+ Set `onBuildError` to use one behaviour for both. `'fail'` stops dev too.
115
+ `'runtime'` lets a production build finish, and the server fetches the policy
116
+ at runtime. The `C15T_ON_BUILD_ERROR` environment variable overrides the
117
+ option, so you can deploy during a backend outage without a code change:
118
+
119
+ ```sh
120
+ C15T_ON_BUILD_ERROR=runtime npm run build
121
+ ```
122
+
123
+ Turborepo's strict environment mode hides undeclared variables from tasks, so
124
+ list `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
125
+
126
+ The build skips the fetch, without an error, when it can't use a snapshot,
127
+ for example when the backend URL is relative. With `onBuildError: 'fail'`, a
128
+ relative URL stops the build. [Consent modes](./modes.md#what-happens-when-the-download-fails)
129
+ lists every case.
130
+
94
131
  For deployments that need policy updates without a rebuild, choose a runtime
95
132
  path:
96
133
 
97
- * **Cached manifest**.
134
+ * **Cached manifest**, `manifest({ source: 'runtime' })`.
98
135
  Your server downloads the project's public policy file, caches it, and
99
136
  resolves each visitor locally. Consent saves still go to the backend.
100
- * **`/init`**. Each visit asks the backend to resolve the visitor. Simpler, but
101
- every page load makes a request to the backend.
137
+ * **`/init`**, `hosted()`. Each visit asks the backend to resolve the visitor.
138
+ Simpler, but every page load makes a request to the backend.
102
139
 
103
140
  [Data fetching](./data-fetching.md) compares them in detail,
104
141
  including proxies and custom transports.
@@ -81,4 +81,4 @@ client render. Set
81
81
 
82
82
  Use effective permissions to gate work and explicit choices to inspect what the
83
83
  visitor confirmed. Read [how consent works](./how-consent-works.md#a-permission-is-not-a-recorded-choice) for that
84
- distinction and [migration](../upgrade-v3.md) for v3 policy configuration.
84
+ distinction and [policies](./policies.md) for v3 policy configuration.
@@ -130,8 +130,11 @@ Forward that configuration intact. Copying an allowed category into a receipt
130
130
  would invent a grant and lose its original confirmation time.
131
131
 
132
132
  Valid v2 records can be read without a startup rewrite. The next explicit action
133
- writes the v3 format. See [migration](../upgrade-v3.md) for expiry, partial saves,
134
- custom transports and backend contract changes.
133
+ writes the v3 format. The upgrade guides for
134
+ [Next.js](https://c15t.com/docs/frameworks/next/upgrade-v3#keep-visitors-existing-choices),
135
+ [React](../frameworks/react/upgrade-v3.md#keep-visitors-existing-choices) and
136
+ [JavaScript](https://c15t.com/docs/frameworks/javascript/upgrade-v3#keep-visitors-existing-choices)
137
+ cover how v3 treats v2 records.
135
138
 
136
139
  ## Keep open tabs in step
137
140
 
@@ -14,14 +14,14 @@ the same protocol, so every path below works with either. Only `offline()`
14
14
  removes backend requests. For a recommendation by framework, start with
15
15
  [choose your setup](./choose-your-setup.md).
16
16
 
17
- | Fetching path | Where policy resolves | Where choices are submitted | Choose it when |
18
- | -------------------------------- | ------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
19
- | Build-time manifest, recommended | Your server or browser resolves a bundled policy snapshot with each visitor's inputs | Inth or your c15t backend | Your framework supports a build integration and policy changes can ship with a rebuild |
20
- | Cached manifest on your server | Your application resolves public policy data with each request's location and signals | Inth or your c15t backend | Policy changes must reach the app without rebuilding, or builds cannot access the backend |
21
- | Regular backend `/init` | The consent backend | The same backend | You want the fewest moving parts, need backend-owned request resolution, or have no application server |
22
- | Manifest in the browser | The browser, using supplied or unknown location | Inth or your c15t backend | You deliberately want client resolution and have planned geography, bundle size and policy refresh |
23
- | Offline | The browser or local runtime, using bundled rules | No backend submission | Local development and tests. Not recommended for production environments. |
24
- | Custom transport | Your implementation | Your implementation | An existing service cannot use the c15t backend protocol |
17
+ | Fetching path | Where policy resolves | Where choices are submitted | Choose it when |
18
+ | --------------------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | Build-time manifest, recommended for server-rendered apps | Your server resolves a bundled policy snapshot with each visitor's inputs | Inth or your c15t backend | Your framework renders on the server, supports a build integration, and policy changes can ship with a rebuild |
20
+ | Cached manifest on your server | Your application resolves public policy data with each request's location and signals | Inth or your c15t backend | Policy changes must reach the app without rebuilding, or builds cannot access the backend |
21
+ | Regular backend `/init` | The consent backend | The same backend | You have no application server, as in a React, Vue or Svelte single-page app, want the fewest moving parts, or need backend-owned request resolution |
22
+ | Manifest in the browser | The browser, using a supplied location, or `/init` when the policy needs one it doesn't have | Inth or your c15t backend | The policy has no location rules or the page knows the visitor's location, and you accept about 3 KB more bundle and a rebuild for policy edits |
23
+ | Offline | The browser or local runtime, using bundled rules | No backend submission | Local development and tests. Not recommended for production environments. |
24
+ | Custom transport | Your implementation | Your implementation | An existing service cannot use the c15t backend protocol |
25
25
 
26
26
  ## What is a consent manifest?
27
27
 
@@ -45,19 +45,58 @@ requires a refresh strategy for cached or build-time manifests.
45
45
  The build fetches your public policy once and bundles it, so the server never
46
46
  fetches it at runtime.
47
47
 
48
+ `manifest()` is the default mode in every server-rendered framework.
49
+ [Consent modes](./modes.md#download-the-manifest-at-build-time)
50
+ covers each framework's build integration and what happens when the
51
+ download fails.
52
+
48
53
  The framework quickstarts include the build integration for supported
49
54
  deployments. Server resolution uses the snapshot without an upstream manifest
50
55
  request. Browser resolution includes the policy and resolver in the client
51
- bundle, and may still request location for regional policies. Static Nuxt and
56
+ bundle, and still calls `/init` for a regional policy when the browser doesn't
57
+ know the visitor's location, so the React, Vue and Svelte quickstarts use
58
+ `hosted()` instead. Static Nuxt and
52
59
  Astro sites and script-tag sites keep the runtime setup from their guides.
53
60
 
54
61
  The snapshot is fixed at build time:
55
62
 
56
63
  * Rebuild after changing policies, translations or vendors. If your CI caches
57
64
  build output, force a fresh build.
58
- * The build must reach the backend. A failed fetch fails the build.
59
65
  * Consent choices still go to the backend.
60
66
 
67
+ The build reads the backend URL from your public backend URL variable when
68
+ the config doesn't pass one: `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js,
69
+ `NUXT_PUBLIC_C15T_BACKEND_URL` in Nuxt, `PUBLIC_C15T_BACKEND_URL` in Astro,
70
+ Svelte and SvelteKit, and `VITE_C15T_BACKEND_URL` in TanStack Start and other
71
+ Vite apps. Each also reads the matching Inth variable, such as
72
+ `NEXT_PUBLIC_INTH_PROJECT_URL`, when the c15t one is unset. See
73
+ [set the backend URL](./modes.md#set-the-backend-url).
74
+
75
+ The fetch waits at most 10 seconds. When it fails, or no backend URL is set,
76
+ every framework does the same thing:
77
+
78
+ | Command | Default when the fetch fails |
79
+ | ------------------------------------------------------------------------- | -------------------------------------------------------- |
80
+ | Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
81
+ | Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning, and the server fetches the policy at runtime. |
82
+
83
+ Set `onBuildError` to use one behaviour for both. `'fail'` stops dev too.
84
+ `'runtime'` lets a production build finish, and the server fetches the policy
85
+ at runtime. The `C15T_ON_BUILD_ERROR` environment variable overrides the
86
+ option, so you can deploy during a backend outage without a code change:
87
+
88
+ ```sh
89
+ C15T_ON_BUILD_ERROR=runtime npm run build
90
+ ```
91
+
92
+ Turborepo's strict environment mode hides undeclared variables from tasks, so
93
+ list `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
94
+
95
+ The build skips the fetch, without an error, when it can't use a snapshot,
96
+ for example when the backend URL is relative. With `onBuildError: 'fail'`, a
97
+ relative URL stops the build. [Consent modes](./modes.md#what-happens-when-the-download-fails)
98
+ lists every case.
99
+
61
100
  ## How manifest mode counts visitors
62
101
 
63
102
  The backend counts visitors from `/init` requests, and manifest resolution
@@ -73,10 +112,10 @@ no report. Set `reportSessions: false` on an adapter to turn it off.
73
112
  Each page load gets a journey id, a random UUID sent on requests c15t already
74
113
  makes:
75
114
 
76
- | Request | Query parameters |
77
- | ---------------- | ------------------------------------------------------------------------------------------------ |
78
- | `GET /init` | `c15tJourney`, `c15tJourneyScope`, `c15tStored` (`1` if a choice or notice dismissal was stored) |
79
- | `POST /subjects` | `c15tJourney`, `c15tJourneyScope` |
115
+ | Request | Query parameters |
116
+ | ---------------- | ------------------------------------------------------------------------------------ |
117
+ | `GET /init` | `journey`, `journeyScope`, `stored` (`1` if a choice or notice dismissal was stored) |
118
+ | `POST /subjects` | `journey`, `journeyScope` |
80
119
 
81
120
  Session reports carry it as `journey: { id, scope, storedChoice, prompt, domain }`.
82
121
  A report and a save with the same `id` belong to one page load. `prompt` is
@@ -96,35 +135,46 @@ Saves replayed after going offline carry none.
96
135
 
97
136
  Set it on the runtime or provider. In Next.js use `defineConsentConfig({ journey })`;
98
137
  in TanStack Start pass the same value to `resolveConsent` and `ConsentRoot`; with
99
- the inline prefetch script, pass it to `buildPrefetchScript`. Vue, Svelte, Astro
138
+ the inline prefetch script, pass it to `buildPrefetchScript` or to
139
+ `consentManifest({ initPrefetch: { journey } })`. Left unset, the script
140
+ continues a `'tab'` journey the tab already holds. A tab's first page has
141
+ none, so its `/init` reports the scope as `'page'`. The id still links it to
142
+ the save. Pass `journey: 'tab'` to the script too if you report on scope. Vue, Svelte, Astro
100
143
  and the script tag use `'page'`. A manifest that resolves locally in the
101
144
  browser reports no init, so it sends no journey.
102
145
 
103
- If you pass `hosted()` a `fetch`, match on the path: URLs now carry a query string.
146
+ If you pass a transport `hosted()` a `fetch`, match on the path: URLs now carry a query string.
104
147
 
105
148
  ## What does regular `/init` do?
106
149
 
107
- `hosted({ url })` uses `${url}/init` for initialization and `${url}/subjects` for
108
- consent submissions. The name `hosted` describes the transport protocol; the URL
109
- can belong to Inth or your own c15t backend.
150
+ `hosted()` uses `${backendURL}/init` for initialization and
151
+ `${backendURL}/subjects` for consent submissions. The name `hosted` describes
152
+ the protocol; the URL can belong to Inth or your own c15t backend.
110
153
 
111
- ```ts title="src/consent-mode.ts"
112
- import { hosted } from 'c15t';
154
+ ```ts title="c15t.config.ts"
155
+ import { defineConsentConfig, hosted } from 'c15t/next';
113
156
 
114
- export function createConsentMode(backendURL: string) {
115
- return hosted({ url: backendURL });
116
- }
157
+ export default defineConsentConfig({ mode: hosted() });
117
158
  ```
118
159
 
119
- Pass the factory to your adapter's `mode` option. For request rendering, use the
120
- adapter's server helper and forward its prepared result to the browser. For
121
- static output, initialize in the browser with a reachable external URL.
160
+ Each framework passes the mode in a different place, and server-rendered
161
+ frameworks take the data `hosted()` from their own package.
162
+ [Consent modes](./modes.md#import-the-factories-from-your-framework-package)
163
+ lists where each one goes.
122
164
 
123
165
  A regular backend `/init` request lets the backend resolve the visitor context.
124
166
  A same-origin URL named `/api/c15t/init` can instead resolve from a cached
125
167
  manifest in your application. The URL name alone does not tell you which path
126
168
  runs.
127
169
 
170
+ In the browser, an inline script in `<head>` can start `/init` while the
171
+ app's JavaScript downloads, and `hosted()` uses that response. Vite apps get
172
+ it from `consentManifest()` by default, Nuxt adds it to pages with
173
+ `ssr: false`, and TanStack Start has `consentPrefetchHead`. Elsewhere, render
174
+ `buildPrefetchScript({ backendURL })` from `c15t` yourself. If c15t assigns
175
+ a banner experiment's arm, give the script the same `experiment`, or
176
+ `hosted()` sends a second `/init` for visitors who have not chosen yet.
177
+
128
178
  ## How do transports and proxies differ?
129
179
 
130
180
  A transport implements initialization, saving and optional record operations.
@@ -133,28 +183,35 @@ resolver or make personalized responses safe to cache.
133
183
 
134
184
  For a same-origin init route that resolves a manifest, the hosted transport can
135
185
  separate policy reads and record writes. With a backend rewrite mounted at
136
- `/api/c15t`, a client mode can use:
186
+ `/api/c15t`, a single-page app can use the transport `hosted()` from
187
+ `c15t`:
137
188
 
138
189
  ```ts title="src/consent-mode.ts"
139
190
  import { hosted } from 'c15t';
140
191
 
141
192
  export const mode = hosted({
142
- url: '/api/c15t',
193
+ backendURL: '/api/c15t',
143
194
  initURL: '/api/c15t/init',
144
- assertDecisionInputs: true,
145
195
  });
146
196
  ```
147
197
 
148
- The route must return the c15t init response contract. `assertDecisionInputs`
149
- binds saves to the resolved policy when init did not return a signed policy
150
- snapshot token. The backend can then reject a stale policy decision. Next.js
151
- sets this option for you when `ConsentRoot` receives a `config` with
152
- `initURL`.
198
+ The route must return the c15t init response contract. c15t adds its inputs to
199
+ `initURL` as query parameters, replacing any of your own with the same name.
200
+ See [query parameters and CORS](https://c15t.com/docs/self-host/api/endpoints#query-parameters-and-cors)
201
+ for the names. Because `initURL` is set, `assertDecisionInputs` defaults to
202
+ `true`: saves carry the policy decision they were made against when init
203
+ returned no signed policy snapshot token, so the backend can reject a save
204
+ made against a stale policy. Server-rendered frameworks do this for you when
205
+ you set `routePrefix`: the browser re-inits through `${routePrefix}/init`.
206
+ Every framework rejects `routePrefix: '/'` when it sets up, because a
207
+ catch-all consent route at the site root would catch every page.
153
208
 
154
209
  The init route resolves policy; the backend rewrite forwards `/api/c15t/subjects`
155
210
  and other record endpoints. Configure both if you choose this optional proxy
156
- variant. The
157
- [Next.js rewrite recipe](https://c15t.com/docs/frameworks/next/optimization#keep-browser-consent-requests-on-your-origin)
211
+ variant. Server-rendered frameworks do both with one option: `proxy: true` in
212
+ Next.js `c15t.config.ts` or TanStack Start's `createConsentStateHandler`,
213
+ with a consent route created with `proxy: true`. The
214
+ [Next.js recipe](https://c15t.com/docs/frameworks/next/optimization#keep-browser-consent-requests-on-your-origin)
158
215
  shows the configuration. A direct absolute backend URL works
159
216
  without a rewrite.
160
217
 
@@ -179,9 +236,8 @@ saves locally. The runtime's persistence module stores the choice in browser
179
236
  storage. There is no backend audit history, cross-device record service or IP
180
237
  geolocation supplied by this transport.
181
238
 
182
- Use your adapter's offline factory so its translations and provider context are
183
- included. This React and Next.js example replaces the hosted mode in an existing
184
- client provider:
239
+ Use your framework's `offline()` so its translations and provider context are
240
+ included. In a React app it replaces the mode passed to `ConsentProvider`:
185
241
 
186
242
  ```ts title="src/consent-mode.ts"
187
243
  import { offline } from 'c15t/react';
@@ -189,6 +245,10 @@ import { offline } from 'c15t/react';
189
245
  export const mode = offline();
190
246
  ```
191
247
 
248
+ In Next.js, TanStack Start, Nuxt, Astro and SvelteKit, pass the data
249
+ `offline()` from the framework package where the config takes `mode`; see
250
+ [consent modes](./modes.md#import-the-factories-from-your-framework-package).
251
+
192
252
  With no `policyRules`, the current offline transport uses the recommended rule
193
253
  pack. Supplying `policyRules` replaces that pack. Unknown country and region are
194
254
  real resolution inputs; offline mode does not discover a visitor's location.