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

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