@c15t/react 3.0.0-alpha.1 → 3.0.0-alpha.11

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 (413) hide show
  1. package/AGENTS.md +97 -70
  2. package/README.md +21 -17
  3. package/SKILL.md +34 -0
  4. package/dist/aggregate-components.js +1 -1
  5. package/dist/chunk-warming.js +1 -1
  6. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  7. package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
  8. package/dist/component-hooks/use-panel-trigger.js +1 -1
  9. package/dist/components/consent-gate/atoms.js +1 -0
  10. package/dist/components/consent-gate/consent-gate.js +2 -0
  11. package/dist/components/consent-gate/frame.js +2 -0
  12. package/dist/components/consent-gate/index.js +2 -0
  13. package/dist/components/consent-preferences-link/consent-preferences-link.js +1 -1
  14. package/dist/components/iab-panel/atoms/card.js +1 -1
  15. package/dist/components/iab-panel/atoms/feature-item.js +2 -0
  16. package/dist/components/iab-panel/atoms/header.js +1 -1
  17. package/dist/components/iab-panel/atoms/purpose-item.js +1 -1
  18. package/dist/components/iab-panel/atoms/root.js +1 -1
  19. package/dist/components/iab-panel/atoms/stack-item.js +1 -1
  20. package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
  21. package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
  22. package/dist/components/iab-panel/iab-panel.js +1 -1
  23. package/dist/components/iab-panel/index.js +1 -1
  24. package/dist/components/iab-prompt/atoms/overlay.js +1 -1
  25. package/dist/components/iab-prompt/atoms/root.js +1 -1
  26. package/dist/components/iab-prompt/iab-prompt.js +1 -1
  27. package/dist/components/panel/atoms/card.js +1 -1
  28. package/dist/components/panel/atoms/overlay.js +1 -1
  29. package/dist/components/panel/atoms/root.js +1 -1
  30. package/dist/components/panel/panel.js +1 -1
  31. package/dist/components/panel-trigger/atoms/button.js +1 -1
  32. package/dist/components/panel-trigger/atoms/icon.js +1 -1
  33. package/dist/components/panel-trigger/atoms/root.js +1 -1
  34. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  35. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -0
  36. package/dist/components/panel-trigger/panel-trigger-toolbar.js +1 -1
  37. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  38. package/dist/components/panel-trigger/toolbar-loader.js +2 -0
  39. package/dist/components/preferences/atoms/accordion.js +1 -1
  40. package/dist/components/preferences/atoms/root.js +1 -1
  41. package/dist/components/preferences/atoms/vendor-list.js +1 -0
  42. package/dist/components/preferences/index.js +1 -1
  43. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  44. package/dist/components/prompt/atoms/overlay.js +1 -1
  45. package/dist/components/prompt/atoms/root.js +1 -1
  46. package/dist/components/prompt/components.js +1 -1
  47. package/dist/components/prompt/policy-actions.js +1 -1
  48. package/dist/components/prompt/prompt.js +1 -1
  49. package/dist/components/shared/dialog-sheets.js +1 -0
  50. package/dist/components/shared/first-paint-sheets.js +1 -0
  51. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  52. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  53. package/dist/components/shared/libs/compose-refs.js +1 -0
  54. package/dist/components/shared/libs/slot.js +1 -1
  55. package/dist/components/shared/primitives/button.js +1 -1
  56. package/dist/components/shared/primitives/legal-links/index.js +1 -1
  57. package/dist/components/shared/streamed-surface.js +2 -0
  58. package/dist/components/shared/surface-styles.js +2 -0
  59. package/dist/components/shared/ui/dialog/dialog.js +1 -1
  60. package/dist/components/shared/ui/icon.js +1 -1
  61. package/dist/components/shared/ui/logo.js +1 -1
  62. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  63. package/dist/consent-gate.js +2 -0
  64. package/dist/consent-theme.js +1 -0
  65. package/dist/context/consent-tracking-context.js +1 -1
  66. package/dist/context/index.js +1 -1
  67. package/dist/context.js +1 -1
  68. package/dist/devtools-launcher.js +2 -0
  69. package/dist/devtools.js +1 -1
  70. package/dist/draft-context.js +2 -0
  71. package/dist/draft.js +1 -1
  72. package/dist/external-iab-context.js +1 -1
  73. package/dist/gpp.js +2 -0
  74. package/dist/headless.js +1 -1
  75. package/dist/hooks/use-color-scheme.js +1 -1
  76. package/dist/hooks/use-committed-ref.js +2 -0
  77. package/dist/hooks/use-focus-trap.js +1 -1
  78. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  79. package/dist/hooks/use-iab-policy-guard.js +1 -0
  80. package/dist/hooks/use-late-entry.js +2 -0
  81. package/dist/hooks.js +1 -1
  82. package/dist/iab/styles.tw3.css +5 -45
  83. package/dist/iab-context.js +1 -1
  84. package/dist/iab-dialog-deferred.js +2 -0
  85. package/dist/iab.js +1 -1
  86. package/dist/index-parts/consent-banner.js +1 -0
  87. package/dist/index-parts/consent-dialog-link.js +1 -0
  88. package/dist/index-parts/consent-dialog-trigger.js +1 -0
  89. package/dist/index-parts/consent-dialog.js +1 -0
  90. package/dist/index-parts/consent-gate.js +1 -0
  91. package/dist/index-parts/core.js +1 -0
  92. package/dist/index-parts/draft.js +1 -0
  93. package/dist/index-parts/frame.js +1 -0
  94. package/dist/index-parts/hooks.js +1 -0
  95. package/dist/index-parts/hosted.js +1 -0
  96. package/dist/index-parts/module-hooks.js +1 -0
  97. package/dist/index-parts/offline.js +1 -0
  98. package/dist/index-parts/presets.js +1 -0
  99. package/dist/index-parts/provider.js +1 -0
  100. package/dist/index-parts/theme.js +1 -0
  101. package/dist/index-parts/translations.js +1 -0
  102. package/dist/index.js +1 -1
  103. package/dist/kernel-selector.js +1 -0
  104. package/dist/modes.js +1 -0
  105. package/dist/module-hooks/iframe-blocker.js +1 -1
  106. package/dist/module-hooks/network-blocker.js +1 -1
  107. package/dist/module-hooks/network-hold.js +2 -0
  108. package/dist/module-hooks/persistence.js +1 -1
  109. package/dist/panel-link.js +1 -1
  110. package/dist/panel-trigger.js +1 -1
  111. package/dist/panel.js +1 -1
  112. package/dist/postcss-tailwind3.js +1 -0
  113. package/dist/preferences.js +1 -2
  114. package/dist/provider.js +1 -1
  115. package/dist/server/fetch-ssr-data.js +1 -1
  116. package/dist/server/headers.js +1 -1
  117. package/dist/server/normalize-url.js +1 -1
  118. package/dist/styles.tw3.css +33 -51
  119. package/dist/transports/hosted.js +1 -0
  120. package/dist/transports/manifest.js +1 -0
  121. package/dist/transports/offline.js +1 -1
  122. package/dist/utils/deferred-module.js +2 -0
  123. package/dist/utils/merge-slot-props.js +1 -1
  124. package/dist/utils/theme-utils.js +1 -1
  125. package/dist/version.js +1 -1
  126. package/dist-types/aggregate-components.d.ts +2 -2
  127. package/dist-types/chunk-warming.d.ts +42 -8
  128. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +5 -5
  129. package/dist-types/component-hooks/use-headless-iab-consent-ui.d.ts +2 -2
  130. package/dist-types/components/consent-gate/atoms.d.ts +11 -0
  131. package/dist-types/components/consent-gate/consent-gate.d.ts +2 -0
  132. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  133. package/dist-types/components/consent-gate/index.d.ts +25 -0
  134. package/dist-types/components/{frame → consent-gate}/types.d.ts +7 -7
  135. package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
  136. package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
  137. package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
  138. package/dist-types/components/iab-panel/atoms/purpose-item.d.ts +5 -2
  139. package/dist-types/components/iab-panel/atoms/stack-item.d.ts +7 -2
  140. package/dist-types/components/iab-panel/atoms/vendor-list.d.ts +1 -1
  141. package/dist-types/components/iab-panel/iab-panel.d.ts +3 -3
  142. package/dist-types/components/iab-panel/index.d.ts +29 -26
  143. package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
  144. package/dist-types/components/iab-prompt/iab-prompt.d.ts +3 -3
  145. package/dist-types/components/iab-prompt/index.d.ts +19 -19
  146. package/dist-types/components/panel/atoms/card.d.ts +5 -4
  147. package/dist-types/components/panel/atoms/overlay.d.ts +1 -1
  148. package/dist-types/components/panel/index.d.ts +8 -8
  149. package/dist-types/components/panel/panel.d.ts +3 -3
  150. package/dist-types/components/panel-link/index.d.ts +1 -1
  151. package/dist-types/components/panel-trigger/atoms/button.d.ts +1 -1
  152. package/dist-types/components/panel-trigger/atoms/icon.d.ts +1 -1
  153. package/dist-types/components/panel-trigger/atoms/root.d.ts +2 -2
  154. package/dist-types/components/panel-trigger/atoms/toolbar.d.ts +2 -2
  155. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +25 -0
  156. package/dist-types/components/panel-trigger/index.d.ts +4 -4
  157. package/dist-types/components/panel-trigger/panel-trigger-toolbar.d.ts +1 -1
  158. package/dist-types/components/panel-trigger/panel-trigger.d.ts +9 -17
  159. package/dist-types/components/panel-trigger/toolbar-loader.d.ts +34 -0
  160. package/dist-types/components/panel-trigger/types.d.ts +1 -1
  161. package/dist-types/components/preferences/atoms/accordion.d.ts +6 -4
  162. package/dist-types/components/preferences/atoms/button.d.ts +1 -1
  163. package/dist-types/components/preferences/atoms/footer.d.ts +1 -1
  164. package/dist-types/components/preferences/atoms/root.d.ts +1 -1
  165. package/dist-types/components/preferences/atoms/vendor-list.d.ts +23 -0
  166. package/dist-types/components/preferences/index.d.ts +14 -12
  167. package/dist-types/components/preferences/policy-actions.d.ts +2 -2
  168. package/dist-types/components/preferences/preferences.d.ts +1 -1
  169. package/dist-types/components/preferences/types.d.ts +1 -1
  170. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  171. package/dist-types/components/prompt/atoms/root.d.ts +4 -0
  172. package/dist-types/components/prompt/components.d.ts +9 -3
  173. package/dist-types/components/prompt/index.d.ts +12 -12
  174. package/dist-types/components/prompt/policy-actions.d.ts +2 -2
  175. package/dist-types/components/prompt/prompt.d.ts +5 -1
  176. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  177. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  178. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  179. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  180. package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
  181. package/dist-types/components/shared/policy-actions.d.ts +2 -2
  182. package/dist-types/components/shared/primitives/box.d.ts +1 -1
  183. package/dist-types/components/shared/primitives/button.d.ts +2 -2
  184. package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
  185. package/dist-types/components/shared/streamed-surface.d.ts +23 -0
  186. package/dist-types/components/shared/surface-styles.d.ts +60 -0
  187. package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
  188. package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
  189. package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
  190. package/dist-types/components/shared/ui/branding.d.ts +1 -1
  191. package/dist-types/components/shared/ui/button/button.d.ts +1 -1
  192. package/dist-types/components/shared/ui/button/index.d.ts +2 -2
  193. package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
  194. package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
  195. package/dist-types/components/shared/ui/icon.d.ts +2 -1
  196. package/dist-types/components/shared/ui/logo.d.ts +0 -10
  197. package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
  198. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +13 -0
  199. package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
  200. package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
  201. package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
  202. package/dist-types/consent-gate.d.ts +1 -0
  203. package/dist-types/consent-theme.d.ts +38 -0
  204. package/dist-types/context/consent-tracking-context.d.ts +10 -0
  205. package/dist-types/context/index.d.ts +3 -3
  206. package/dist-types/context/theme-context.d.ts +3 -3
  207. package/dist-types/context.d.ts +25 -1
  208. package/dist-types/devtools-launcher.d.ts +29 -0
  209. package/dist-types/devtools.d.ts +13 -2
  210. package/dist-types/draft-context.d.ts +31 -0
  211. package/dist-types/draft.d.ts +81 -9
  212. package/dist-types/gpp.d.ts +27 -0
  213. package/dist-types/headless.d.ts +6 -7
  214. package/dist-types/hooks/use-committed-ref.d.ts +24 -0
  215. package/dist-types/hooks/use-focus-trap.d.ts +3 -1
  216. package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
  217. package/dist-types/hooks/use-iab-manager.d.ts +5 -5
  218. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  219. package/dist-types/hooks/use-late-entry.d.ts +14 -0
  220. package/dist-types/hooks/use-theme.d.ts +1 -1
  221. package/dist-types/hooks.d.ts +106 -11
  222. package/dist-types/iab-dialog-deferred.d.ts +36 -0
  223. package/dist-types/iab.d.ts +8 -7
  224. package/dist-types/index-parts/consent-banner.d.ts +7 -0
  225. package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
  226. package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
  227. package/dist-types/index-parts/consent-dialog.d.ts +7 -0
  228. package/dist-types/index-parts/consent-gate.d.ts +7 -0
  229. package/dist-types/index-parts/core.d.ts +7 -0
  230. package/dist-types/index-parts/draft.d.ts +7 -0
  231. package/dist-types/index-parts/frame.d.ts +7 -0
  232. package/dist-types/index-parts/hooks.d.ts +7 -0
  233. package/dist-types/index-parts/hosted.d.ts +7 -0
  234. package/dist-types/index-parts/module-hooks.d.ts +7 -0
  235. package/dist-types/index-parts/offline.d.ts +7 -0
  236. package/dist-types/index-parts/presets.d.ts +7 -0
  237. package/dist-types/index-parts/provider.d.ts +7 -0
  238. package/dist-types/index-parts/theme.d.ts +8 -0
  239. package/dist-types/index-parts/translations.d.ts +7 -0
  240. package/dist-types/index.d.ts +32 -28
  241. package/dist-types/kernel-selector.d.ts +68 -0
  242. package/dist-types/modes.d.ts +26 -0
  243. package/dist-types/module-hooks/iframe-blocker.d.ts +33 -1
  244. package/dist-types/module-hooks/network-blocker.d.ts +23 -0
  245. package/dist-types/module-hooks/network-hold.d.ts +47 -0
  246. package/dist-types/module-hooks.d.ts +4 -4
  247. package/dist-types/panel-link.d.ts +1 -1
  248. package/dist-types/panel-trigger.d.ts +1 -1
  249. package/dist-types/panel.d.ts +13 -1
  250. package/dist-types/postcss-tailwind3.d.ts +21 -0
  251. package/dist-types/preferences.d.ts +12 -1
  252. package/dist-types/primitives/accordion.d.ts +1 -1
  253. package/dist-types/primitives/button.d.ts +1 -1
  254. package/dist-types/primitives/collapsible.d.ts +1 -1
  255. package/dist-types/primitives/dialog.d.ts +1 -1
  256. package/dist-types/primitives/preference-item.d.ts +1 -1
  257. package/dist-types/primitives/switch.d.ts +1 -1
  258. package/dist-types/primitives/tabs.d.ts +1 -1
  259. package/dist-types/primitives.d.ts +7 -7
  260. package/dist-types/prompt.d.ts +1 -1
  261. package/dist-types/provider.d.ts +169 -28
  262. package/dist-types/server/fetch-ssr-data.d.ts +1 -1
  263. package/dist-types/server/headers.d.ts +14 -4
  264. package/dist-types/server/index.d.ts +5 -5
  265. package/dist-types/server/normalize-url.d.ts +14 -7
  266. package/dist-types/server/types.d.ts +12 -1
  267. package/dist-types/theme-provider.d.ts +2 -2
  268. package/dist-types/transports/hosted.d.ts +36 -0
  269. package/dist-types/transports/manifest.d.ts +48 -0
  270. package/dist-types/transports/offline.d.ts +7 -16
  271. package/dist-types/types/index.d.ts +3 -3
  272. package/dist-types/types/manager.d.ts +1 -1
  273. package/dist-types/types/theme/index.d.ts +1 -1
  274. package/dist-types/ui-config-context.d.ts +14 -2
  275. package/dist-types/utils/deferred-module.d.ts +13 -0
  276. package/dist-types/utils/index.d.ts +4 -4
  277. package/dist-types/utils/merge-slot-props.d.ts +17 -3
  278. package/dist-types/utils/merge-styles.d.ts +1 -1
  279. package/dist-types/utils/theme-utils.d.ts +18 -3
  280. package/dist-types/version.d.ts +1 -1
  281. package/docs/README.md +97 -70
  282. package/docs/assets/v3/bottom-bar.png +0 -0
  283. package/docs/assets/v3/brand-card.png +0 -0
  284. package/docs/assets/v3/brand-preferences.png +0 -0
  285. package/docs/assets/v3/choice-wall.png +0 -0
  286. package/docs/assets/v3/headless-bar-html.png +0 -0
  287. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  288. package/docs/assets/v3/headless-bar.png +0 -0
  289. package/docs/assets/v3/slim-bar.png +0 -0
  290. package/docs/concepts/choose-your-setup.md +141 -0
  291. package/docs/concepts/consent-categories.md +84 -0
  292. package/docs/concepts/consent-state.md +421 -0
  293. package/docs/concepts/data-fetching.md +284 -0
  294. package/docs/concepts/how-consent-works.md +123 -0
  295. package/docs/concepts/modes.md +281 -0
  296. package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +8 -60
  297. package/docs/customization/class-names.md +202 -0
  298. package/docs/customization/dark-mode.md +157 -0
  299. package/docs/customization/motion.md +128 -0
  300. package/docs/customization/overview.md +68 -33
  301. package/docs/customization/recipes.md +838 -47
  302. package/docs/customization/slots.md +217 -35
  303. package/docs/customization/stylesheets.md +248 -0
  304. package/docs/customization/tailwind.md +863 -0
  305. package/docs/customization/tokens.md +167 -36
  306. package/docs/customization/translations.md +87 -3
  307. package/docs/frameworks/react/callbacks.md +118 -0
  308. package/docs/frameworks/react/clear-on-revocation.md +106 -0
  309. package/docs/frameworks/react/components/consent-banner.md +48 -27
  310. package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
  311. package/docs/frameworks/react/components/consent-dialog-trigger.md +86 -76
  312. package/docs/frameworks/react/components/consent-dialog.md +111 -58
  313. package/docs/frameworks/react/components/{frame.md → consent-gate.md} +70 -49
  314. package/docs/frameworks/react/components/consent-provider.md +143 -0
  315. package/docs/frameworks/react/components/consent-widget.md +69 -24
  316. package/docs/frameworks/react/components/dev-tools.md +76 -85
  317. package/docs/frameworks/react/components.md +113 -0
  318. package/docs/frameworks/react/compose.md +354 -0
  319. package/docs/frameworks/react/content-security-policy.md +149 -0
  320. package/docs/frameworks/react/customize.md +258 -0
  321. package/docs/frameworks/react/embeds.md +89 -0
  322. package/docs/frameworks/react/gpp.md +163 -0
  323. package/docs/frameworks/react/headless.md +4 -4
  324. package/docs/frameworks/react/hooks.md +70 -0
  325. package/docs/frameworks/react/iab.md +278 -0
  326. package/docs/frameworks/react/network-blocker.md +140 -0
  327. package/docs/frameworks/react/quickstart.md +171 -107
  328. package/docs/frameworks/react/rendering.md +285 -0
  329. package/docs/frameworks/react/scripts.md +114 -0
  330. package/docs/frameworks/react/translations.md +155 -0
  331. package/docs/frameworks/react/troubleshooting.md +189 -61
  332. package/docs/frameworks/react/upgrade-v3.md +652 -0
  333. package/docs/frameworks/react/vendor-consent.md +188 -0
  334. package/docs/guides/banner-experiments.md +688 -0
  335. package/docs/guides/troubleshooting.md +192 -54
  336. package/docs/guides/verify-consent.md +96 -49
  337. package/docs/integrations/adobe-analytics.md +177 -183
  338. package/docs/integrations/ahrefs-analytics.md +162 -178
  339. package/docs/integrations/amplitude.md +172 -180
  340. package/docs/integrations/building-integrations.md +137 -33
  341. package/docs/integrations/clearbit.md +164 -178
  342. package/docs/integrations/cloudflare-web-analytics.md +166 -180
  343. package/docs/integrations/cloudflare-zaraz.md +333 -0
  344. package/docs/integrations/crisp.md +174 -182
  345. package/docs/integrations/databuddy.md +167 -197
  346. package/docs/integrations/fathom-analytics.md +168 -180
  347. package/docs/integrations/front-chat.md +308 -0
  348. package/docs/integrations/google-maps.md +142 -108
  349. package/docs/integrations/google-tag-manager.md +258 -185
  350. package/docs/integrations/google-tag.md +226 -182
  351. package/docs/integrations/heap.md +173 -179
  352. package/docs/integrations/hightouch.md +171 -181
  353. package/docs/integrations/hotjar.md +169 -179
  354. package/docs/integrations/intercom.md +193 -177
  355. package/docs/integrations/klaviyo.md +473 -0
  356. package/docs/integrations/linkedin-insights.md +184 -174
  357. package/docs/integrations/logrocket.md +170 -180
  358. package/docs/integrations/matomo-analytics.md +198 -202
  359. package/docs/integrations/meta-pixel.md +198 -174
  360. package/docs/integrations/microsoft-clarity.md +173 -179
  361. package/docs/integrations/microsoft-uet.md +158 -178
  362. package/docs/integrations/mixpanel-analytics.md +165 -184
  363. package/docs/integrations/one-dollar-stats.md +292 -0
  364. package/docs/integrations/openai-pixel.md +214 -325
  365. package/docs/integrations/overview.md +153 -83
  366. package/docs/integrations/pinterest-tag.md +315 -0
  367. package/docs/integrations/pirsch.md +179 -183
  368. package/docs/integrations/plausible-analytics.md +182 -182
  369. package/docs/integrations/posthog.md +323 -264
  370. package/docs/integrations/promptwatch.md +164 -178
  371. package/docs/integrations/reddit-pixel.md +195 -181
  372. package/docs/integrations/rudderstack.md +211 -210
  373. package/docs/integrations/rybbit-analytics.md +181 -184
  374. package/docs/integrations/segment.md +192 -178
  375. package/docs/integrations/sentry.md +402 -0
  376. package/docs/integrations/snapchat-pixel.md +196 -180
  377. package/docs/integrations/tiktok-pixel.md +181 -174
  378. package/docs/integrations/umami-analytics.md +173 -182
  379. package/docs/integrations/vercel-analytics.md +176 -181
  380. package/docs/integrations/vercel-speed-insights.md +306 -0
  381. package/docs/integrations/x-pixel.md +186 -174
  382. package/docs/integrations/youtube.md +150 -111
  383. package/package.json +48 -17
  384. package/dist/component-hooks/use-manager.js +0 -2
  385. package/dist/components/frame/atoms.js +0 -1
  386. package/dist/components/frame/frame.js +0 -2
  387. package/dist/components/frame/index.js +0 -2
  388. package/dist/frame.js +0 -2
  389. package/dist/ui-save.js +0 -1
  390. package/dist-types/component-hooks/use-manager.d.ts +0 -40
  391. package/dist-types/components/frame/atoms.d.ts +0 -11
  392. package/dist-types/components/frame/frame.d.ts +0 -2
  393. package/dist-types/components/frame/index.d.ts +0 -24
  394. package/dist-types/frame.d.ts +0 -1
  395. package/dist-types/ui-save.d.ts +0 -5
  396. package/docs/assets/v3/brand-bar.png +0 -0
  397. package/docs/assets/v3/mobile-card.png +0 -0
  398. package/docs/assets/v3/preferences.png +0 -0
  399. package/docs/frameworks/react/components/consent-manager-provider.md +0 -71
  400. package/docs/frameworks/react/concepts/consent-categories.md +0 -33
  401. package/docs/frameworks/react/hooks/use-consent-manager/overview.md +0 -58
  402. package/docs/frameworks/react/iab/overview.md +0 -53
  403. package/docs/frameworks/react/script-loader.md +0 -63
  404. package/docs/frameworks/react/styling/overview.md +0 -217
  405. package/docs/guides/consent-state.md +0 -60
  406. package/docs/guides/data-fetching.md +0 -163
  407. package/docs/guides/deployment-modes.md +0 -63
  408. package/docs/integrations/clear-on-revocation.md +0 -167
  409. package/docs/upgrade-v3.md +0 -334
  410. package/readme.json +0 -48
  411. package/src/iab/styles.css +0 -11
  412. package/src/styles.css +0 -10
  413. /package/dist/components/{frame → consent-gate}/types.js +0 -0
@@ -1,143 +1,207 @@
1
1
  ---
2
2
  title: Quickstart
3
- description: Connect a React application to Inth and add consent UI, persistence
4
- and a preferences link.
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
- ## Connect Inth
8
+ ## Before you start
9
9
 
10
- [Create an Inth project](https://inth.com), configure policy rules and the app's
11
- allowed origin, and copy its backend endpoint. This guide uses a client-rendered
12
- React app with Vite. Applications with a framework server should use that
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 and asks your consent backend for the visitor's policy, then
12
+ shows the banner or runs the scripts the visitor allowed. The backend reads the
13
+ visitor's location from the request, so regional policies need no extra setup.
14
14
 
15
- | Package manager | Command |
16
- | :-------------- | :------------------------------- |
17
- | npm | `npm install c15t @c15t/scripts` |
18
- | pnpm | `pnpm add c15t @c15t/scripts` |
19
- | yarn | `yarn add c15t @c15t/scripts` |
20
- | bun | `bun add c15t @c15t/scripts` |
15
+ * **Next.js or TanStack Start?** Use that framework's guide from the
16
+ [framework list](https://v3.c15t.com/docs/frameworks). Their adapters resolve consent on the
17
+ server.
18
+ * **React Router framework mode, Remix or another server-rendered React app?**
19
+ This guide still works. The server renders the page without consent state and
20
+ the browser resolves it after hydration. Read
21
+ [rendering](./rendering.md#react-router-remix-and-other-server-rendered-apps)
22
+ for what that means for the first paint.
21
23
 
22
- Set `VITE_C15T_BACKEND_URL` to the endpoint from Inth. Vite embeds public values
23
- at build time, so rebuild when changing it. Keep management credentials out of
24
- public variables.
24
+ You need an [Inth](https://inth.com) project. In the project, set the policy
25
+ rules for your regions, add your site's origin to the trusted origins, and copy
26
+ the backend URL.
27
+ [Choose your setup](../../concepts/choose-your-setup.md#decide-who-runs-the-consent-backend)
28
+ covers self-hosting and offline mode.
25
29
 
26
- ## Register consent-gated scripts
30
+ The guide uses `hosted()` mode. Policy, translation and vendor edits in your
31
+ project reach visitors on their next page load, without a rebuild.
27
32
 
28
- Include measurement and marketing categories in your policy for these vendors.
29
- Remove any existing loaders for them so each script loads once.
33
+ ## Install c15t
30
34
 
31
- This setup uses Vite's public environment variables. Set the Inth endpoint and
32
- add your PostHog project key, PostHog API host and X Pixel ID in `.env.local`:
35
+ | Package manager | Command |
36
+ | :-------------- | :------------------------------------------------ |
37
+ | npm | `npm install c15t@alpha @c15t/integrations@alpha` |
38
+ | pnpm | `pnpm add c15t@alpha @c15t/integrations@alpha` |
39
+ | yarn | `yarn add c15t@alpha @c15t/integrations@alpha` |
40
+ | bun | `bun add c15t@alpha @c15t/integrations@alpha` |
33
41
 
34
- ```dotenv title=".env.local"
35
- VITE_C15T_BACKEND_URL=
36
- VITE_POSTHOG_KEY=
37
- VITE_POSTHOG_HOST=
38
- VITE_X_PIXEL_ID=
42
+ `c15t` contains the React adapter at `c15t/react` and the Vite plugin at
43
+ `c15t/build`. `@c15t/integrations` holds the vendor loaders used below.
44
+
45
+ ## Add the Vite plugin
46
+
47
+ Add `consentManifest` to `vite.config.ts`:
48
+
49
+ ```ts title="vite.config.ts"
50
+ import react from '@vitejs/plugin-react';
51
+ import { consentManifest } from 'c15t/build';
52
+ import { defineConfig } from 'vite';
53
+
54
+ export default defineConfig({ plugins: [react(), consentManifest()] });
39
55
  ```
40
56
 
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.
45
-
46
- Create `src/scripts.ts`. PostHog waits for measurement permission with
47
- `loadMode: 'after-consent'` and disables cookieless capture with
48
- `cookieless_mode: 'never'`. X Pixel waits for marketing permission.
49
-
50
- ```ts title="src/scripts.ts"
51
- import { posthog } from '@c15t/scripts/posthog';
52
- import { xPixel } from '@c15t/scripts/x-pixel';
53
-
54
- const posthogKey = import.meta.env.VITE_POSTHOG_KEY;
55
- const xPixelId = import.meta.env.VITE_X_PIXEL_ID;
56
-
57
- export const scripts = [
58
- ...(posthogKey
59
- ? [
60
- posthog({
61
- apiHost: import.meta.env.VITE_POSTHOG_HOST,
62
- id: posthogKey,
63
- initOptions: { cookieless_mode: 'never' },
64
- loadMode: 'after-consent',
65
- }),
66
- ]
67
- : []),
68
- ...(xPixelId ? [xPixel({ pixelId: xPixelId })] : []),
69
- ];
57
+ The plugin reads `VITE_C15T_BACKEND_URL`. Set it to your project's backend URL,
58
+ exactly as Inth shows it, including any path prefix, in `.env` or wherever you
59
+ build:
60
+
61
+ ```sh title=".env"
62
+ VITE_C15T_BACKEND_URL=https://your-project.inth.app
70
63
  ```
71
64
 
72
- ## Mount the provider and UI
65
+ If your app already sets `VITE_INTH_PROJECT_URL` for other Inth SDKs, that works
66
+ too. When both are set, `VITE_C15T_BACKEND_URL` wins.
67
+
68
+ `consentManifest` hands the backend URL to `hosted()` through
69
+ `c15t/generated`. No file is written into your project. Without the plugin,
70
+ pass the URL as `hosted({ backendURL })`.
71
+
72
+ The plugin also adds a small script to `index.html` that sends `/init` while
73
+ your JavaScript downloads, so the banner shows sooner.
74
+ [Rendering](./rendering.md#send-init-before-the-app-loads)
75
+ covers when to turn it off.
76
+
77
+ ## Mount the provider
78
+
79
+ Create `src/consent.tsx`. `ConsentProvider` creates the consent runtime and
80
+ loads the scripts. `ConsentBanner` and `ConsentDialog` render when the policy
81
+ asks for them, and `ConsentDialogLink` keeps preferences reachable after the
82
+ banner closes.
73
83
 
74
84
  ```tsx title="src/consent.tsx"
75
- import type { ReactNode } from 'react';
85
+ import { posthog } from '@c15t/integrations/posthog';
76
86
  import {
77
- ConsentBanner,
78
- ConsentDialog,
79
- ConsentDialogLink,
80
- ConsentProvider,
81
- hosted,
87
+ ConsentBanner,
88
+ ConsentDialog,
89
+ ConsentDialogLink,
90
+ ConsentProvider,
91
+ hosted,
82
92
  } from 'c15t/react';
83
- import 'c15t/react/styles.css';
84
- import { scripts } from './scripts';
85
-
86
- const backendURL = import.meta.env.VITE_C15T_BACKEND_URL;
87
- if (!backendURL) throw new Error('Set VITE_C15T_BACKEND_URL');
88
- const mode = hosted({ url: backendURL });
89
-
90
- export function Consent({ children }: { children: ReactNode }) {
91
- return (
92
- <ConsentProvider options={{ mode, scripts }}>
93
- {children}
94
- <ConsentBanner />
95
- <ConsentDialog />
96
- <footer><ConsentDialogLink>Privacy settings</ConsentDialogLink></footer>
97
- </ConsentProvider>
98
- );
99
- }
93
+ import type { ReactNode } from 'react';
94
+
95
+ const options = {
96
+ // Asks VITE_C15T_BACKEND_URL for each visitor's policy.
97
+ mode: hosted(),
98
+ scripts: [
99
+ posthog({
100
+ id: 'phc_your_project_key',
101
+ initOptions: { cookieless_mode: 'never' },
102
+ loadMode: 'after-consent',
103
+ }),
104
+ ],
105
+ };
106
+
107
+ export const Consent = ({ children }: { children: ReactNode }) => (
108
+ <ConsentProvider options={options}>
109
+ {children}
110
+ <ConsentBanner />
111
+ <ConsentDialog />
112
+ <footer>
113
+ <ConsentDialogLink>Privacy settings</ConsentDialogLink>
114
+ </footer>
115
+ </ConsentProvider>
116
+ );
100
117
  ```
101
118
 
119
+ `hosted()` makes one `/init` request to your backend, started early by the
120
+ script in `index.html`. The backend picks the policy from the visitor's
121
+ location and language and returns its current version. Consent choices go to
122
+ the same backend. Vite reads `VITE_C15T_BACKEND_URL` at build time, so set it
123
+ before you build, not when you serve the files.
124
+
125
+ PostHog waits for measurement permission, so your policy must include that
126
+ category. Replace `phc_your_project_key` with your PostHog project key.
127
+ PostHog uses its EU host; add `region: 'us'` for a US project. Remove any
128
+ other PostHog loader from your app so PostHog loads once.
129
+
130
+ `loadMode: 'after-consent'` keeps the PostHog SDK off the page until the
131
+ visitor allows measurement. `cookieless_mode: 'never'` turns off PostHog's
132
+ cookieless capture. See [integrations](../../integrations/overview.md) for other
133
+ vendors.
134
+
135
+ Wrap your app with it in `src/main.tsx`:
136
+
102
137
  ```tsx title="src/main.tsx"
103
138
  import { createRoot } from 'react-dom/client';
104
- import App from './App';
139
+
140
+ import { App } from './app';
105
141
  import { Consent } from './consent';
106
142
 
107
143
  const root = document.getElementById('root');
108
- if (!root) throw new Error('Missing #root element');
109
- createRoot(root).render(<Consent><App /></Consent>);
144
+ if (!root) {
145
+ throw new Error('Missing #root element');
146
+ }
147
+ createRoot(root).render(
148
+ <Consent>
149
+ <App />
150
+ </Consent>
151
+ );
110
152
  ```
111
153
 
112
- Keep `Consent` mounted across navigation. The banner appears when the resolved
113
- policy requires a prompt; not every region or returning visitor needs one.
114
- The dialog link keeps preferences reachable after the prompt closes.
154
+ Keep `Consent` mounted for the life of the app. If you use a client-side
155
+ router, render the router inside `Consent` so navigation does not remount the
156
+ provider. There is no stylesheet to import: the banner and dialog render their
157
+ own rules as `<style>` elements.
115
158
 
116
- ## Gate optional functionality
159
+ ## Gate your own features
117
160
 
118
- ```tsx
161
+ Read a category's permission with `useConsent` inside the provider:
162
+
163
+ ```tsx title="src/marketing-banner.tsx"
119
164
  import { useConsent } from 'c15t/react';
120
165
 
121
- export function MarketingContent() {
122
- return useConsent('marketing') ? <div>Optional marketing content</div> : null;
166
+ export function MarketingBanner() {
167
+ return useConsent('marketing') ? <aside>Spring sale</aside> : null;
123
168
  }
124
169
  ```
125
170
 
126
- This controls the rendered component. Move vendor SDKs into the
127
- [script loader](./script-loader.md) too. An independently
128
- loaded analytics script does not wait merely because a consent provider exists.
129
-
130
- `useConsent` returns effective permission, not proof of an explicit grant.
131
- Read [consent state](../../guides/consent-state.md) when recording choice events.
132
-
133
- ## Verify and customize
134
-
135
- Reject under an opt-in policy, reload and confirm optional requests stay blocked.
136
- Reopen preferences, save a different choice and test navigation. Follow
137
- [verification](../../guides/verify-consent.md), then
138
- [customization](../../customization/overview.md) to apply your design.
139
-
140
- ## Run the complete example
141
-
142
- Try the [runnable example](https://c15t.com/docs/examples) with PostHog, X Pixel, a consent-gated
143
- YouTube video and a persistent preferences control. Use it to test rejection, permission changes and reloads.
171
+ `useConsent` returns the permission right now. Under an opt-out policy it can be
172
+ `true` before the visitor has chosen anything. Read
173
+ [how consent works](../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice)
174
+ before you record or report choices. For iframes, use
175
+ [`ConsentGate`](./components/consent-gate.md).
176
+
177
+ ## Check that it works
178
+
179
+ Build and preview the production bundle with `vite build` and `vite preview`,
180
+ then open the site in a private window with DevTools open on the Network tab.
181
+ Test under a policy that asks for a choice, such as an EU opt-in policy.
182
+
183
+ 1. **Before a choice.** One `/init` request goes to your backend URL, starting
184
+ before your JavaScript finishes downloading, and the banner shows. There
185
+ are no requests to `posthog.com`.
186
+ 2. **Reject All.** The banner closes and the vendor requests stay absent.
187
+ 3. **Reload.** The banner stays closed and the vendor requests stay absent.
188
+ 4. **Privacy settings.** The footer link opens the dialog with **Analytics** (the
189
+ `measurement` category) and **Marketing** off. Turn on **Analytics** and
190
+ save. PostHog's `array.js` loads.
191
+ 5. **Turn Analytics off again.** The page reloads and PostHog does not load.
192
+
193
+ If the banner never appears, check the `/init` request to your backend URL. [Troubleshooting](./troubleshooting.md)
194
+ covers the common causes. [Verify consent](../../guides/verify-consent.md) has the
195
+ full release checklist.
196
+
197
+ ## Next steps
198
+
199
+ * [Rendering](./rendering.md) for server-rendered React apps and
200
+ what the first paint contains.
201
+ * [Scripts and embeds](./scripts.md) for more vendors, network
202
+ blocking and data cleanup.
203
+ * [Customize](./customize.md) for colors, fonts, layout and copy.
204
+ * [Components](./components/consent-provider.md) for
205
+ every provider option and component prop.
206
+ * The runnable app in `examples/react` of the
207
+ [c15t repository](https://github.com/c15t/c15t) contains this setup.
@@ -0,0 +1,285 @@
1
+ ---
2
+ title: Rendering and deployment
3
+ description: How c15t resolves consent in client-rendered React apps, React
4
+ Router framework mode, Remix and other server-rendered React apps without a
5
+ c15t adapter.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Pick your rendering path
10
+
11
+ `c15t/react` resolves consent in the browser. That works for every React app,
12
+ including server-rendered ones. What changes is what the first HTML contains.
13
+
14
+ | Your app | Where consent resolves | Setup |
15
+ | ------------------------------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------- |
16
+ | Vite, or another client-rendered single-page app | Browser, after the bundle loads | [Quickstart](./quickstart.md) |
17
+ | Prerendered or static HTML | Browser, after the page loads | [Quickstart](./quickstart.md), see [static pages](#static-and-prerendered-pages) |
18
+ | React Router framework mode, Remix, or another server-rendered React app | Browser, after hydration | [Quickstart](./quickstart.md), see [server-rendered apps](#react-router-remix-and-other-server-rendered-apps) |
19
+ | Next.js or TanStack Start | Server, per request | That framework's guide in the [framework list](https://v3.c15t.com/docs/frameworks) |
20
+
21
+ Pick the Next.js or TanStack Start adapter if you use those frameworks. They
22
+ read the visitor's cookie and location on the server, so the banner can be in
23
+ the first HTML. [Choose your setup](../../concepts/choose-your-setup.md#server-rendering-or-browser-rendering)
24
+ explains the difference in general terms.
25
+
26
+ ## Client-rendered apps
27
+
28
+ The [quickstart](./quickstart.md) and `examples/react` in
29
+ the c15t repository use `hosted()` from `c15t/react`. It reads the backend URL
30
+ `consentManifest` read from `VITE_C15T_BACKEND_URL` or
31
+ `VITE_INTH_PROJECT_URL`; without the plugin, pass `hosted({ backendURL })`.
32
+ Policy edits apply on the next page load, without a rebuild. The page loads
33
+ like this:
34
+
35
+ 1. The page loads with no consent state. Every optional category is denied.
36
+ 2. The browser requests `GET /init` from your backend URL. A
37
+ [script in `index.html`](#send-init-before-the-app-loads) sends it while
38
+ the JavaScript downloads. The backend picks the visitor's policy from their
39
+ location. With an `experiment`, pass it to the plugin as
40
+ `initPrefetch: { experiment }` so the early request carries the visitor's
41
+ arm. Without that, the provider sends its own `/init` when it mounts.
42
+ 3. The provider applies the policy and any stored choice. It shows the banner
43
+ if the policy asks for one, and loads the scripts the visitor allowed.
44
+
45
+ Until step 3 finishes, gated scripts and `ConsentGate` embeds stay blocked. If
46
+ the backend request fails, they stay blocked and no banner shows.
47
+
48
+ ### Send `/init` before the app loads
49
+
50
+ `consentManifest()` adds a small inline script to the `<head>` of
51
+ `index.html`. It sends `/init` while the browser downloads your JavaScript,
52
+ and `hosted()` uses that response instead of sending its own. The banner
53
+ usually shows 50 to 250 ms sooner. There is nothing to configure.
54
+
55
+ The early request can't read your app's options, so in a few setups it won't
56
+ match and the app sends a second `/init`. Pass `initPrefetch: false` when the
57
+ app uses `offline()` or gives `hosted()` its own `backendURL`, `fetch`,
58
+ `initURL` or `headers`. With its own `fetch`, `hosted()` sends `/init` through
59
+ it and ignores the early response. If it sets `credentials`, `overrides`, `storageKey`, an
60
+ `experiment`, or `journey: false`, pass the same values as
61
+ `initPrefetch: { credentials, experiment, journey, overrides, storageKey }`.
62
+ With `journey: 'tab'`, passing it too makes a tab's first `/init` report the
63
+ `'tab'` scope instead of `'page'`. It doesn't save a request.
64
+ Pass `experiment` only when c15t assigns the arm. If a feature flag picks
65
+ it, leave it out, or the script sends an arm the visitor may never see.
66
+ The dev server adds the script even to a `manifest()` app, which then sends
67
+ one `/init` per page load that it doesn't use. A production build leaves it
68
+ out.
69
+
70
+ ## React Router, Remix and other server-rendered apps
71
+
72
+ React Router framework mode, Remix and similar frameworks have no c15t adapter.
73
+ Use the quickstart's `Consent` component and render it in your root component,
74
+ around the outlet. In React Router framework mode that is `app/root.tsx`.
75
+
76
+ `ConsentProvider` renders on the server without touching browser APIs, and the
77
+ browser takes over after hydration:
78
+
79
+ * The server HTML contains your page but no banner and no consent state.
80
+ * After hydration the browser calls `/init`, then shows the banner if the
81
+ policy asks for one.
82
+ * The server render and the first browser render agree, so hydration does not
83
+ warn or change a stored choice.
84
+
85
+ The Vite setup needs no change. The banner renders in the browser and brings
86
+ its own `<style>` element, so there is no stylesheet to import.
87
+
88
+ These frameworks render their own HTML, so the Vite plugin can't add the
89
+ early `/init` script. Put `buildPrefetchScript({ backendURL })` from `c15t` in
90
+ a `<script>` in the root `<head>` instead, unless your loader supplies
91
+ `initialData` as described below. The two are alternatives: with
92
+ `initialData` the provider never reads the script's response.
93
+
94
+ ### Skip the browser's first init request
95
+
96
+ This is optional. `fetchSSRData` from `c15t/react/server` calls the backend's
97
+ `/init` from your server loader, forwarding the visitor's location and
98
+ language headers. Pass the result to the browser, wrap it in a promise and give
99
+ it to `hosted({ backendURL, initialData })`. The provider's first init then uses that
100
+ response instead of a network request.
101
+
102
+ Know its limits before you add it:
103
+
104
+ * It saves the browser's round trip to the backend. It does not put the banner
105
+ in the server HTML; the banner still mounts after hydration.
106
+ * Every page view makes a server-to-server `/init` request. Do not cache the
107
+ loader response, because it contains one visitor's policy.
108
+ * It returns `undefined` when the request has no location or language headers,
109
+ and the browser then requests `/init` as usual.
110
+ * `mode` is read once, when the provider mounts. Create the `hosted()` mode once
111
+ in the browser rather than on every render.
112
+ * A relative `backendURL` resolves against the request's `host` header, over
113
+ `https` for a domain name and over `http` for `localhost`, an IP address or
114
+ a single-label host. `fetchSSRData` ignores `x-forwarded-host`,
115
+ `x-forwarded-proto` and `forwarded`, and does not send them to the backend.
116
+ Behind a proxy that overwrites those headers, pass
117
+ `trustForwardedHeaders: true` to use them.
118
+ * If your server answers requests for any `Host` header, such as a Node server
119
+ exposed directly or a proxy that forwards `Host` from its default virtual
120
+ host, pass `fetchSSRData` an absolute `backendURL`. A relative one resolves
121
+ against whatever `Host` the request sent.
122
+
123
+ ### Render the banner in the server HTML
124
+
125
+ c15t has no framework-neutral helper for this. `ConsentProvider` accepts a
126
+ server-resolved `prefetch` state, but the Next.js and TanStack Start adapters
127
+ build that state from the request's cookie, headers and a cached policy
128
+ manifest. Building it by hand ties your app to internal shapes. If the banner
129
+ must be part of the first paint, use one of those adapters. Otherwise keep the
130
+ browser path.
131
+
132
+ ## Static and prerendered pages
133
+
134
+ A prerendered page is the same HTML for every visitor, so it cannot contain one
135
+ visitor's consent. Use the quickstart's setup. `/init` requests and consent
136
+ saves go to the backend URL directly, so the static host needs no server
137
+ route. Add the site's
138
+ origin to the trusted origins in your Inth project.
139
+
140
+ Do not cache a response that contains a visitor's consent in a CDN or a shared
141
+ page cache. [Data fetching](../../concepts/data-fetching.md) covers what is safe to
142
+ cache.
143
+
144
+ ## Where the browser sends requests
145
+
146
+ With `hosted()`, the browser calls two backend endpoints:
147
+
148
+ | Request | When |
149
+ | --------------------- | ------------------------------------------ |
150
+ | `GET {url}/init` | Once per page load, to resolve the policy |
151
+ | `POST {url}/subjects` | When the visitor accepts, rejects or saves |
152
+
153
+ These requests go to the backend's origin, so it must list your site as a
154
+ trusted origin.
155
+ [Data fetching](../../concepts/data-fetching.md) compares this with proxies and
156
+ custom transports.
157
+
158
+ ## Check the rendering path
159
+
160
+ 1. Open the page in a private window and view the page source. In a
161
+ server-rendered app the HTML contains your content and no element with
162
+ `data-testid="consent-banner-root"`.
163
+ 2. In DevTools Network, confirm one `/init` request runs, unless you supply
164
+ `initialData`.
165
+ 3. The banner appears once the policy resolves. Vendor requests stay absent
166
+ until you allow their category.
167
+
168
+ ## Bundle the policy at build time
169
+
170
+ `manifest()` resolves the policy in the browser from a snapshot the build
171
+ downloaded. It saves the `/init` request only in these cases:
172
+
173
+ * The policy has no location rules, or every location gets the same banner.
174
+ * The page already knows the visitor's location, for example from an edge
175
+ worker, and passes it as `inputs`.
176
+ * You run a same-origin `geoURL` route that returns the location.
177
+
178
+ Otherwise the browser still calls `/init` on a first visit and uses the
179
+ backend's answer, so the bundle adds bytes without saving a request.
180
+ `consentManifest` warns during the build when that happens. The resolver adds
181
+ about 3 KB gzipped to the bundle, and policy edits need a rebuild. The browser
182
+ sends no `/init`, so the backend doesn't count visitors it resolves this way.
183
+
184
+ The build fetches your public policy once and bundles it, so the server never
185
+ fetches it at runtime.
186
+
187
+ Keep `consentManifest` in your Vite plugin list:
188
+
189
+ ```ts title="vite.config.ts (plugins option)"
190
+ import { consentManifest } from 'c15t/build';
191
+
192
+ export default {
193
+ plugins: [
194
+ // Keep your existing framework plugins here.
195
+ consentManifest({ backendURL: 'https://your-project.inth.app' }),
196
+ ],
197
+ };
198
+ ```
199
+
200
+ Without `backendURL`, the plugin reads `VITE_C15T_BACKEND_URL`, then
201
+ `VITE_INTH_PROJECT_URL`. The plugin
202
+ fetches the policy during `vite build`, or in `vite dev` when the app first
203
+ loads it, and serves it as `snapshot` from `c15t/generated`. A build fetches
204
+ only when the app uses `manifest()`. In `src/consent.tsx`, create the
205
+ provider's mode with `manifest()` from `c15t/react` instead of `hosted()`. It
206
+ reads the snapshot and the backend URL from `c15t/generated`:
207
+
208
+ ```tsx title="src/consent.tsx"
209
+ import { manifest } from 'c15t/react';
210
+
211
+ const mode = manifest();
212
+ ```
213
+
214
+ Keep passing `mode` to `ConsentProvider`. The browser resolves the snapshot
215
+ without `/manifest` when it has the geography the policy needs, or the policy
216
+ does not depend on geography. Supply known country and region through `inputs`
217
+ or overrides to keep regional resolution on the snapshot.
218
+
219
+ When the build has no snapshot, as in `vite dev` after a failed fetch,
220
+ `snapshot` is `undefined`, and the browser fetches
221
+ `${backendURL}/manifest` instead.
222
+
223
+ When the required country or region is missing, the transport calls backend
224
+ `/init` and uses its resolved policy and translations. That response can use
225
+ newer backend policy than the build snapshot. `ConsentProvider` sends that
226
+ request while it first renders, before the page mounts, unless the visitor has
227
+ a stored choice.
228
+
229
+ The browser cannot guess the banner from the snapshot alone when locations
230
+ differ. With a European opt-in rule and no banner elsewhere, a banner shown
231
+ before `/init` answers would vanish for most of the world. The exception is a
232
+ policy whose country and region rules all show the same banner, with a default
233
+ rule that covers everywhere else: the browser resolves it at once.
234
+
235
+ Policy and resolver code are part of the browser bundle.
236
+
237
+ The snapshot is fixed at build time:
238
+
239
+ * Rebuild after changing policies, translations or vendors. If your CI caches
240
+ build output, force a fresh build.
241
+ * Consent choices still go to the backend.
242
+
243
+ The build reads the backend URL from your public backend URL variable when
244
+ the config doesn't pass one: `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js,
245
+ `NUXT_PUBLIC_C15T_BACKEND_URL` in Nuxt, `PUBLIC_C15T_BACKEND_URL` in Astro,
246
+ Svelte and SvelteKit, and `VITE_C15T_BACKEND_URL` in TanStack Start and other
247
+ Vite apps. Each also reads the matching Inth variable, such as
248
+ `NEXT_PUBLIC_INTH_PROJECT_URL`, when the c15t one is unset. See
249
+ [set the backend URL](../../concepts/modes.md#set-the-backend-url).
250
+
251
+ The fetch waits at most 10 seconds. When it fails, or no backend URL is set,
252
+ every framework does the same thing:
253
+
254
+ | Command | Default when the fetch fails |
255
+ | ------------------------------------------------------------------------- | -------------------------------------------------------- |
256
+ | Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
257
+ | Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning, and the server fetches the policy at runtime. |
258
+
259
+ Set `onBuildError` to use one behaviour for both. `'fail'` stops dev too.
260
+ `'runtime'` lets a production build finish, and the server fetches the policy
261
+ at runtime. The `C15T_ON_BUILD_ERROR` environment variable overrides the
262
+ option, so you can deploy during a backend outage without a code change:
263
+
264
+ ```sh
265
+ C15T_ON_BUILD_ERROR=runtime npm run build
266
+ ```
267
+
268
+ Turborepo's strict environment mode hides undeclared variables from tasks, so
269
+ list `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
270
+
271
+ The build skips the fetch, without an error, when it can't use a snapshot,
272
+ for example when the backend URL is relative. With `onBuildError: 'fail'`, a
273
+ relative URL stops the build. [Consent modes](../../concepts/modes.md#what-happens-when-the-download-fails)
274
+ lists every case.
275
+
276
+ `vite build` and `vite dev` fetch the manifest when they start. `vite preview`
277
+ serves the last build without fetching. The plugin writes no file into your
278
+ app, so there is nothing to keep out of Git. `c15t/generated` ships its own
279
+ types, so `tsc`, `vue-tsc` and `svelte-check` pass on a fresh checkout without
280
+ a build first.
281
+
282
+ The plugin can't see the options your app passes to `manifest()`. When the
283
+ app passes `source: 'runtime'` or `manifestURL`, set
284
+ `consentManifest({ source: 'runtime' })` too. The build then downloads no
285
+ manifest, so it doesn't fail when the backend's `/manifest` is down.