@c15t/nextjs 2.2.0 → 3.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/AGENTS.md +102 -142
  2. package/README.md +4 -4
  3. package/dist/api.js +1 -0
  4. package/dist/config.js +1 -0
  5. package/dist/devtools.js +2 -0
  6. package/dist/headers.js +1 -0
  7. package/dist/iab/styles.css +1 -1
  8. package/dist/iab/styles.tw3.css +133 -19
  9. package/dist/index.js +1 -1
  10. package/dist/middleware.js +1 -0
  11. package/dist/node-bridge.js +1 -0
  12. package/dist/pages.js +1 -0
  13. package/dist/proxy.js +1 -0
  14. package/dist/root.js +2 -0
  15. package/dist/server.js +1 -0
  16. package/dist/static.js +1 -0
  17. package/dist/styles.css +1 -1
  18. package/dist/styles.tw3.css +173 -29
  19. package/dist/version.js +1 -1
  20. package/dist-types/api.d.ts +88 -0
  21. package/dist-types/config.d.ts +110 -0
  22. package/dist-types/devtools.d.ts +1 -0
  23. package/dist-types/headers.d.ts +4 -0
  24. package/dist-types/index.d.ts +28 -10
  25. package/dist-types/middleware.d.ts +15 -0
  26. package/dist-types/node-bridge.d.ts +57 -0
  27. package/dist-types/pages.d.ts +88 -0
  28. package/dist-types/proxy.d.ts +34 -0
  29. package/dist-types/root.d.ts +86 -0
  30. package/dist-types/server.d.ts +164 -0
  31. package/dist-types/static.d.ts +42 -0
  32. package/dist-types/types.d.ts +5 -36
  33. package/dist-types/version.d.ts +1 -1
  34. package/docs/README.md +102 -142
  35. package/docs/assets/v3/brand-bar.png +0 -0
  36. package/docs/assets/v3/brand-card.png +0 -0
  37. package/docs/assets/v3/choice-wall.png +0 -0
  38. package/docs/assets/v3/mobile-card.png +0 -0
  39. package/docs/assets/v3/preferences.png +0 -0
  40. package/docs/customization/overview.md +45 -0
  41. package/docs/customization/recipes.md +79 -0
  42. package/docs/customization/slots.md +55 -0
  43. package/docs/customization/tokens.md +76 -0
  44. package/docs/customization/translations.md +49 -0
  45. package/docs/frameworks/next/api-reference/data-fetching.md +416 -0
  46. package/docs/frameworks/next/app-router.md +403 -0
  47. package/docs/frameworks/next/client-side.md +118 -0
  48. package/docs/frameworks/next/components/consent-banner.md +251 -211
  49. package/docs/frameworks/next/components/consent-dialog-link.md +96 -35
  50. package/docs/frameworks/next/components/consent-dialog-trigger.md +74 -149
  51. package/docs/frameworks/next/components/consent-dialog.md +189 -134
  52. package/docs/frameworks/next/components/consent-manager-provider.md +60 -318
  53. package/docs/frameworks/next/components/consent-widget.md +172 -114
  54. package/docs/frameworks/next/components/dev-tools.md +199 -40
  55. package/docs/frameworks/next/components/frame.md +137 -42
  56. package/docs/frameworks/next/concepts/consent-categories.md +24 -89
  57. package/docs/frameworks/next/concepts/policy-presets.md +142 -0
  58. package/docs/frameworks/next/content-security-policy.md +189 -0
  59. package/docs/frameworks/next/data-fetching.md +74 -0
  60. package/docs/frameworks/next/geography-headers.md +251 -0
  61. package/docs/frameworks/next/headless.md +95 -185
  62. package/docs/frameworks/next/hooks/use-consent-manager/overview.md +42 -163
  63. package/docs/frameworks/next/iab/overview.md +37 -107
  64. package/docs/frameworks/next/optimization.md +158 -194
  65. package/docs/frameworks/next/pages-router.md +296 -0
  66. package/docs/frameworks/next/quickstart.md +31 -132
  67. package/docs/frameworks/next/script-loader.md +134 -467
  68. package/docs/frameworks/next/server-side.md +97 -130
  69. package/docs/frameworks/next/static-export.md +164 -0
  70. package/docs/frameworks/next/styling/overview.md +166 -250
  71. package/docs/frameworks/next/troubleshooting.md +134 -144
  72. package/docs/guides/consent-state.md +60 -0
  73. package/docs/guides/data-fetching.md +163 -0
  74. package/docs/guides/deployment-modes.md +63 -0
  75. package/docs/guides/troubleshooting.md +68 -0
  76. package/docs/guides/verify-consent.md +62 -0
  77. package/docs/integrations/adobe-analytics.md +239 -105
  78. package/docs/integrations/ahrefs-analytics.md +238 -104
  79. package/docs/integrations/amplitude.md +219 -157
  80. package/docs/integrations/building-integrations.md +32 -224
  81. package/docs/integrations/clearbit.md +247 -86
  82. package/docs/integrations/cloudflare-web-analytics.md +250 -84
  83. package/docs/integrations/crisp.md +251 -97
  84. package/docs/integrations/databuddy.md +259 -153
  85. package/docs/integrations/fathom-analytics.md +239 -96
  86. package/docs/integrations/google-maps.md +328 -207
  87. package/docs/integrations/google-tag-manager.md +248 -96
  88. package/docs/integrations/google-tag.md +261 -90
  89. package/docs/integrations/heap.md +222 -149
  90. package/docs/integrations/hightouch.md +225 -131
  91. package/docs/integrations/hotjar.md +239 -90
  92. package/docs/integrations/intercom.md +239 -98
  93. package/docs/integrations/linkedin-insights.md +243 -113
  94. package/docs/integrations/logrocket.md +241 -123
  95. package/docs/integrations/matomo-analytics.md +256 -111
  96. package/docs/integrations/meta-pixel.md +197 -324
  97. package/docs/integrations/microsoft-clarity.md +233 -114
  98. package/docs/integrations/microsoft-uet.md +245 -110
  99. package/docs/integrations/mixpanel-analytics.md +252 -87
  100. package/docs/integrations/openai-pixel.md +441 -0
  101. package/docs/integrations/overview.md +95 -133
  102. package/docs/integrations/pirsch.md +249 -96
  103. package/docs/integrations/plausible-analytics.md +241 -100
  104. package/docs/integrations/posthog.md +353 -214
  105. package/docs/integrations/promptwatch.md +251 -81
  106. package/docs/integrations/reddit-pixel.md +226 -173
  107. package/docs/integrations/rudderstack.md +244 -187
  108. package/docs/integrations/rybbit-analytics.md +244 -91
  109. package/docs/integrations/segment.md +238 -92
  110. package/docs/integrations/snapchat-pixel.md +240 -110
  111. package/docs/integrations/tiktok-pixel.md +249 -81
  112. package/docs/integrations/umami-analytics.md +242 -95
  113. package/docs/integrations/vercel-analytics.md +242 -90
  114. package/docs/integrations/x-pixel.md +238 -104
  115. package/docs/integrations/youtube.md +354 -142
  116. package/docs/upgrade-v3.md +334 -0
  117. package/iab/styles.css +1 -1
  118. package/iab/styles.tw3.css +1 -1
  119. package/package.json +106 -65
  120. package/readme.json +3 -3
  121. package/src/iab/styles.css +1 -1
  122. package/src/iab/styles.tw3.css +1 -1
  123. package/src/styles.css +1 -1
  124. package/src/styles.tw3.css +1 -1
  125. package/styles.css +1 -1
  126. package/styles.tw3.css +1 -1
  127. package/client/components/consent-dialog-link.js +0 -3
  128. package/client/components/integrations.js +0 -3
  129. package/dist/components/integrations/index.cjs +0 -1
  130. package/dist/components/integrations/index.js +0 -1
  131. package/dist/headless.cjs +0 -1
  132. package/dist/index.cjs +0 -1
  133. package/dist/libs/browser-initial-data.cjs +0 -1
  134. package/dist/libs/browser-initial-data.js +0 -1
  135. package/dist/libs/initial-data.cjs +0 -1
  136. package/dist/libs/initial-data.js +0 -1
  137. package/dist/types.cjs +0 -1
  138. package/dist/version.cjs +0 -1
  139. package/dist-types/components/integrations/index.d.ts +0 -1
  140. package/dist-types/libs/browser-initial-data.d.ts +0 -9
  141. package/dist-types/libs/initial-data.d.ts +0 -33
  142. package/docs/frameworks/next/building-headless-components.md +0 -379
  143. package/docs/frameworks/next/callbacks.md +0 -186
  144. package/docs/frameworks/next/concepts/client-modes.md +0 -177
  145. package/docs/frameworks/next/concepts/consent-models.md +0 -117
  146. package/docs/frameworks/next/concepts/cookie-management.md +0 -122
  147. package/docs/frameworks/next/concepts/glossary.md +0 -24
  148. package/docs/frameworks/next/concepts/initialization-flow.md +0 -149
  149. package/docs/frameworks/next/concepts/policy-packs.md +0 -230
  150. package/docs/frameworks/next/hooks/use-color-scheme.md +0 -41
  151. package/docs/frameworks/next/hooks/use-consent-manager/checking-consent.md +0 -96
  152. package/docs/frameworks/next/hooks/use-consent-manager/location-info.md +0 -97
  153. package/docs/frameworks/next/hooks/use-consent-manager/setting-consent.md +0 -94
  154. package/docs/frameworks/next/hooks/use-draggable.md +0 -59
  155. package/docs/frameworks/next/hooks/use-focus-trap.md +0 -42
  156. package/docs/frameworks/next/hooks/use-reduced-motion.md +0 -37
  157. package/docs/frameworks/next/hooks/use-ssr-status.md +0 -32
  158. package/docs/frameworks/next/hooks/use-text-direction.md +0 -50
  159. package/docs/frameworks/next/hooks/use-translations.md +0 -55
  160. package/docs/frameworks/next/iab/consent-banner.md +0 -91
  161. package/docs/frameworks/next/iab/consent-dialog.md +0 -129
  162. package/docs/frameworks/next/iab/use-gvl-data.md +0 -21
  163. package/docs/frameworks/next/iframe-blocking.md +0 -106
  164. package/docs/frameworks/next/internationalization.md +0 -207
  165. package/docs/frameworks/next/network-blocker.md +0 -140
  166. package/docs/frameworks/next/policy-packs.md +0 -248
  167. package/docs/frameworks/next/styling/classnames.md +0 -94
  168. package/docs/frameworks/next/styling/color-scheme.md +0 -84
  169. package/docs/frameworks/next/styling/css-variables.md +0 -53
  170. package/docs/frameworks/next/styling/slots.md +0 -94
  171. package/docs/frameworks/next/styling/tailwind.md +0 -137
  172. package/docs/frameworks/next/styling/tokens.md +0 -156
  173. package/docs/shared/concepts/client-modes.md +0 -103
  174. package/docs/shared/concepts/consent-categories.md +0 -41
  175. package/docs/shared/concepts/consent-models.md +0 -72
  176. package/docs/shared/concepts/cookie-management.md +0 -88
  177. package/docs/shared/concepts/glossary.md +0 -24
  178. package/docs/shared/concepts/initialization-flow.md +0 -105
  179. package/docs/shared/concepts/policy-packs.md +0 -225
  180. package/docs/shared/react/components/consent-banner.md +0 -242
  181. package/docs/shared/react/components/consent-dialog-link.md +0 -45
  182. package/docs/shared/react/components/consent-dialog-trigger.md +0 -185
  183. package/docs/shared/react/components/consent-dialog.md +0 -119
  184. package/docs/shared/react/components/consent-manager-provider.md +0 -225
  185. package/docs/shared/react/components/consent-widget.md +0 -121
  186. package/docs/shared/react/components/dev-tools.md +0 -81
  187. package/docs/shared/react/components/frame.md +0 -52
  188. package/docs/shared/react/guides/building-headless-components.md +0 -110
  189. package/docs/shared/react/guides/callbacks.md +0 -89
  190. package/docs/shared/react/guides/headless.md +0 -31
  191. package/docs/shared/react/guides/iframe-blocking.md +0 -65
  192. package/docs/shared/react/guides/internationalization.md +0 -123
  193. package/docs/shared/react/guides/network-blocker.md +0 -72
  194. package/docs/shared/react/guides/optimization.md +0 -44
  195. package/docs/shared/react/guides/policy-packs.md +0 -173
  196. package/docs/shared/react/guides/script-loader.md +0 -311
  197. package/docs/shared/react/hooks/use-color-scheme.md +0 -31
  198. package/docs/shared/react/hooks/use-consent-manager/checking-consent.md +0 -95
  199. package/docs/shared/react/hooks/use-consent-manager/location-info.md +0 -96
  200. package/docs/shared/react/hooks/use-consent-manager/overview.md +0 -74
  201. package/docs/shared/react/hooks/use-consent-manager/setting-consent.md +0 -93
  202. package/docs/shared/react/hooks/use-draggable.md +0 -30
  203. package/docs/shared/react/hooks/use-focus-trap.md +0 -20
  204. package/docs/shared/react/hooks/use-reduced-motion.md +0 -33
  205. package/docs/shared/react/hooks/use-ssr-status.md +0 -16
  206. package/docs/shared/react/hooks/use-text-direction.md +0 -38
  207. package/docs/shared/react/hooks/use-translations.md +0 -15
  208. package/docs/shared/react/iab/consent-banner.md +0 -60
  209. package/docs/shared/react/iab/consent-dialog.md +0 -76
  210. package/docs/shared/react/iab/overview.md +0 -80
  211. package/docs/shared/react/iab/use-gvl-data.md +0 -21
  212. package/docs/shared/react/styling/classnames.md +0 -93
  213. package/docs/shared/react/styling/color-scheme.md +0 -35
  214. package/docs/shared/react/styling/css-variables.md +0 -53
  215. package/docs/shared/react/styling/overview.md +0 -261
  216. package/docs/shared/react/styling/slots.md +0 -93
  217. package/docs/shared/react/styling/stylesheet-entrypoint.md +0 -8
  218. package/docs/shared/react/styling/tailwind.md +0 -88
  219. package/docs/shared/react/styling/tokens.md +0 -155
  220. package/docs/shared/troubleshooting.md +0 -82
package/dist/index.cjs DELETED
@@ -1 +0,0 @@
1
- "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_modules__={76(e){e.exports=require("./libs/browser-initial-data.cjs")},91(e){e.exports=require("./libs/initial-data.cjs")},289(e){e.exports=require("@c15t/react")},285(e){e.exports=require("c15t")}},__webpack_module_cache__={};function __webpack_require__(e){var _=__webpack_module_cache__[e];if(void 0!==_)return _.exports;var r=__webpack_module_cache__[e]={exports:{}};return __webpack_modules__[e](r,r.exports,__webpack_require__),r.exports}__webpack_require__.n=e=>{var _=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(_,{a:_}),_},__webpack_require__.d=(e,_,r)=>{var t=(_,r)=>{for(var t in _)__webpack_require__.o(_,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,[r]:_[t]})};t(_,"get"),t(r,"value")},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};for(var __rspack_i in(()=>{__webpack_require__.r(__webpack_exports__);var e=__webpack_require__(289),_={};for(let r in e)0>["fetchInitialData","default","buildPrefetchScript","C15tPrefetch"].indexOf(r)&&(_[r]=()=>e[r]);__webpack_require__.d(__webpack_exports__,_);var r=__webpack_require__(285),t=__webpack_require__(76),a=__webpack_require__(91);__webpack_require__.d(__webpack_exports__,{C15tPrefetch:()=>t.C15tPrefetch,buildPrefetchScript:()=>r.buildPrefetchScript,fetchInitialData:()=>a.fetchInitialData})})(),exports.C15tPrefetch=__webpack_exports__.C15tPrefetch,exports.buildPrefetchScript=__webpack_exports__.buildPrefetchScript,exports.fetchInitialData=__webpack_exports__.fetchInitialData,__webpack_exports__)-1===["C15tPrefetch","buildPrefetchScript","fetchInitialData"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
@@ -1 +0,0 @@
1
- "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.n=e=>{var _=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(_,{a:_}),_},__webpack_require__.d=(e,_,r)=>{var t=(_,r)=>{for(var t in _)__webpack_require__.o(_,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,[r]:_[t]})};t(_,"get"),t(r,"value")},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{C15tPrefetch:()=>C15tPrefetch});const jsx_runtime_namespaceObject=require("react/jsx-runtime"),external_c15t_namespaceObject=require("c15t"),script_namespaceObject=require("next/script");var script_default=__webpack_require__.n(script_namespaceObject);const DEFAULT_SCRIPT_ID="c15t-initial-data-prefetch";function C15tPrefetch({id:e="c15t-initial-data-prefetch",..._}){return(0,jsx_runtime_namespaceObject.jsx)(script_default(),{id:e,strategy:"beforeInteractive",children:(0,external_c15t_namespaceObject.buildPrefetchScript)(_)})}for(var __rspack_i in exports.C15tPrefetch=__webpack_exports__.C15tPrefetch,__webpack_exports__)-1===["C15tPrefetch"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
@@ -1 +0,0 @@
1
- import{jsx as t}from"react/jsx-runtime";import{buildPrefetchScript as r}from"c15t";import e from"next/script";function i({id:c="c15t-initial-data-prefetch",...o}){return t(e,{id:c,strategy:"beforeInteractive",children:r(o)})}export{i as C15tPrefetch};
@@ -1 +0,0 @@
1
- "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,_,r)=>{var a=(_,r)=>{for(var a in _)__webpack_require__.o(_,a)&&!__webpack_require__.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,[r]:_[a]})};a(_,"get"),a(r,"value")},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{fetchInitialData:()=>fetchInitialData});const server_namespaceObject=require("@c15t/react/server"),cache_namespaceObject=require("next/cache"),headers_namespaceObject=require("next/headers"),DEFAULT_REVALIDATE_SECONDS=1;async function fetchInitialData(e){let _=await (0,headers_namespaceObject.headers)(),r=(0,server_namespaceObject.normalizeBackendURL)(e.backendURL,_);if(!r)return;let a=e.nextCache?.revalidateSeconds;if(!1===a)return(0,server_namespaceObject.fetchSSRData)({...e,backendURL:r,headers:_});let t=(0,server_namespaceObject.createSSRInitCacheKey)({normalizedURL:r,headers:_,overrides:e.overrides});return(0,cache_namespaceObject.unstable_cache)(()=>(0,server_namespaceObject.fetchSSRData)({...e,backendURL:r,headers:_}),["c15t:nextjs:fetchInitialData",t],{revalidate:a??1})()}for(var __rspack_i in exports.fetchInitialData=__webpack_exports__.fetchInitialData,__webpack_exports__)-1===["fetchInitialData"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
@@ -1 +0,0 @@
1
- import{createSSRInitCacheKey as e,fetchSSRData as t,normalizeBackendURL as a}from"@c15t/react/server";import{unstable_cache as r}from"next/cache";import{headers as n}from"next/headers";async function c(c){let i=await n(),o=a(c.backendURL,i);if(!o)return;let d=c.nextCache?.revalidateSeconds;return!1===d?t({...c,backendURL:o,headers:i}):r(()=>t({...c,backendURL:o,headers:i}),["c15t:nextjs:fetchInitialData",e({normalizedURL:o,headers:i,overrides:c.overrides})],{revalidate:d??1})()}export{c as fetchInitialData};
package/dist/types.cjs DELETED
@@ -1 +0,0 @@
1
- "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};for(var __rspack_i in __webpack_require__.r(__webpack_exports__),__webpack_exports__)exports[__rspack_i]=__webpack_exports__[__rspack_i];Object.defineProperty(exports,"__esModule",{value:!0});
package/dist/version.cjs DELETED
@@ -1 +0,0 @@
1
- "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,_,r)=>{var o=(_,r)=>{for(var o in _)__webpack_require__.o(_,o)&&!__webpack_require__.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,[r]:_[o]})};o(_,"get"),o(r,"value")},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__);const version="2.2.0";for(var __rspack_i in __webpack_require__.d(__webpack_exports__,{},{version:"2.2.0"}),exports.version=__webpack_exports__.version,__webpack_exports__)-1===["version"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
@@ -1 +0,0 @@
1
- export * from '@c15t/react/components/integrations';
@@ -1,9 +0,0 @@
1
- import type { C15tPrefetchProps } from '../types';
2
- /**
3
- * Next.js script component that starts `/init` prefetching before hydration.
4
- *
5
- * @remarks
6
- * Use in `app/layout.tsx` for static routes. Matching prefetched data is
7
- * consumed automatically by the runtime during first store initialization.
8
- */
9
- export declare function C15tPrefetch({ id, ...options }: C15tPrefetchProps): import("react").JSX.Element;
@@ -1,33 +0,0 @@
1
- import type { SSRInitialData } from 'c15t';
2
- import type { FetchInitialDataOptions } from '../types';
3
- /**
4
- * Fetches initial consent data on the server for SSR hydration.
5
- *
6
- * **IMPORTANT: Do NOT await this function in Server Components.**
7
- * Pass the Promise directly to your client component, which passes it
8
- * to ConsentManagerProvider's `ssrData` option. The provider awaits
9
- * it internally, allowing Next.js to stream the response while data loads.
10
- *
11
- * @param options - Configuration options
12
- * @param options.backendURL - Backend URL (absolute or relative)
13
- * @param options.overrides - Optional geo-location overrides
14
- * @param options.debug - Enable debug logging
15
- * @returns The SSR initial data promise
16
- *
17
- * @example
18
- * ```tsx
19
- * // Server Component (layout.tsx)
20
- * const initialData = fetchInitialData({
21
- * backendURL: '/api/consent',
22
- * debug: process.env.NODE_ENV === 'development'
23
- * });
24
- * return (
25
- * <ConsentManagerProvider
26
- * options={{ mode: 'hosted', backendURL: '/api/consent', ssrData: initialData }}
27
- * >
28
- * {children}
29
- * </ConsentManagerProvider>
30
- * )
31
- * ```
32
- */
33
- export declare function fetchInitialData(options: FetchInitialDataOptions): Promise<SSRInitialData | undefined>;
@@ -1,379 +0,0 @@
1
- ---
2
- title: Building Headless Components
3
- description: Build policy-aware custom consent components in Next.js using the
4
- headless hooks and policy-pack tooling.
5
- group: frameworks
6
- ---
7
- Building custom consent UI is easier now because c15t exposes multiple layers of policy-aware primitives instead of forcing you to reconstruct banner rules by hand.
8
-
9
- Think of customization as a ladder:
10
-
11
- * stock component props for the shortest path
12
- * `ConsentBanner.PolicyActions` and `ConsentWidget.PolicyActions` when you want custom structure but still want c15t to resolve policy-aware actions
13
- * `useHeadlessConsentUI()` when you need fully manual action rendering, custom controls, or non-standard flow
14
-
15
- > ⚠️ **Warning:**
16
- > Headless is the last step in the customization ladder. Use this guide only when pre-built components, tokens, slots, compound components, and noStyle are no longer sufficient.
17
-
18
- The headless stack underneath that is:
19
-
20
- * `useHeadlessConsentUI()` for policy-aware banner/dialog actions, ordering, layout, and primary actions hints
21
- * `@c15t/ui/utils` for the pure policy-action helpers that framework packages build on
22
- * `useConsentManager()` for runtime state, categories, selected consent state, and policy metadata
23
- * `useTranslations()` for the resolved copy
24
- * `offlinePolicy.policyPacks` for offline previews that behave like backend policy resolution
25
-
26
- The split is intentional: `@c15t/ui` owns pure policy-action resolution, while the framework hooks own visibility, consent mutations, and reactive state.
27
-
28
- > ℹ️ **Info:**
29
- > This guide is about building your own components while still respecting resolved policy-pack behavior. For the general headless overview, see Headless Mode.
30
-
31
- ## Choose the Smallest Layer That Solves the Job
32
-
33
- Start with the smallest API surface that still gives you the behavior you need:
34
-
35
- * Stay with stock components when you only need theming, spacing, copy, or legal-link changes
36
- * Use `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions` when you want a custom compound-component layout but still want grouped actions, ordering, and primary emphasis to come from policy
37
- * Add `renderAction` when the grouping is still correct but you want to remap actions to stock c15t button compounds
38
- * Reach for `useHeadlessConsentUI()` only when you need custom button elements, need to map `actionGroups` yourself, wire non-button controls, or coordinate the consent UI with a more custom state machine
39
-
40
- This order matters because every step down the ladder gives you more control, but also makes it easier for your UI to drift away from the resolved policy if you stop using the provided state.
41
-
42
- ## Before You Build Headless UI
43
-
44
- Do not use headless mode for problems that are still inside the stock component model:
45
-
46
- * Use `layout`, `direction`, `primaryButton`, and `legalLinks` before you rebuild banner markup
47
- * Use `theme.consentActions` before you swap out stock actions
48
- * Use tokens such as `colors.surface` and `colors.surfaceHover` before raw CSS overrides
49
- * Use slots such as `consentBannerCard`, `consentBannerFooter`, and `consentDialogCard` before compound components
50
- * Use `ConsentManagerProvider.options.i18n` before rebuilding UI just to change text
51
-
52
- A good rule: if the stock banner or dialog structure is still correct, you probably do not need headless mode.
53
-
54
- ## What the Headless Tooling Gives You
55
-
56
- The main win is that your custom UI can stay aligned with policy packs without duplicating policy logic in your components.
57
-
58
- `useHeadlessConsentUI()` already resolves:
59
-
60
- * which actions are allowed
61
- * the order those actions should render in
62
- * grouped actions from policy `layout`
63
- * layout `direction` (`row` or `column`)
64
- * the primary actions
65
- * UI profile and scroll-lock hints
66
- * whether the banner or dialog should currently be visible
67
-
68
- The hook also gives you the policy-aware action helpers you are expected to call:
69
-
70
- * `performBannerAction('accept' | 'reject')`
71
- * `performDialogAction('accept' | 'reject')`
72
- * `saveCustomPreferences()` for the dialog `customize` action
73
- * `openDialog()`, `openBanner()`, and `closeUI()` for surface visibility
74
-
75
- That means your component mostly focuses on markup and design-system concerns instead of re-implementing policy interpretation.
76
-
77
- For most compound-component layouts, start with `ConsentBanner.PolicyActions` or `ConsentWidget.PolicyActions`. They render stock c15t buttons and translations by default, and `renderAction` is only needed when you want to override which stock compound renders for each action. Reach for manual `actionGroups` mapping when you need action rendering that no longer fits the stock button compounds.
78
-
79
- ## Policy-Aware Compound Components First
80
-
81
- If your goal is "custom layout, same policy behavior", start here before dropping to manual `actionGroups` rendering:
82
-
83
- ```tsx title="components/consent-manager/banner-shell.tsx"
84
- 'use client';
85
-
86
- import { ConsentBanner } from '@c15t/nextjs';
87
-
88
- export function BannerShell() {
89
- return (
90
- <ConsentBanner.Root>
91
- <ConsentBanner.Card>
92
- <ConsentBanner.Header>
93
- <ConsentBanner.Title />
94
- <ConsentBanner.Description />
95
- </ConsentBanner.Header>
96
- <ConsentBanner.PolicyActions />
97
- </ConsentBanner.Card>
98
- </ConsentBanner.Root>
99
- );
100
- }
101
- ```
102
-
103
- Use `renderAction` only when you want to remap actions to stock button compounds while keeping the same policy-driven grouping and ordering:
104
-
105
- ```tsx title="components/consent-manager/banner-actions.tsx"
106
- 'use client';
107
-
108
- import { ConsentBanner } from '@c15t/nextjs';
109
-
110
- export function BannerActionsWithCustomMapping() {
111
- return (
112
- <ConsentBanner.PolicyActions
113
- renderAction={(action, props) => {
114
- const { key, ...buttonProps } = props;
115
-
116
- switch (action) {
117
- case 'accept':
118
- return <ConsentBanner.AcceptButton key={key} {...buttonProps} />;
119
- case 'reject':
120
- return <ConsentBanner.RejectButton key={key} {...buttonProps} />;
121
- case 'customize':
122
- return <ConsentBanner.CustomizeButton key={key} {...buttonProps} />;
123
- }
124
- }}
125
- />
126
- );
127
- }
128
- ```
129
-
130
- > ℹ️ **Info:**
131
- > For custom layouts built from c15t compound components, prefer ConsentBanner.PolicyActions and ConsentWidget.PolicyActions. The examples below intentionally use manual actionGroups mapping to show the fully headless escape hatch.
132
-
133
- ## Provider Setup for Local Policy Testing
134
-
135
- ```tsx title="components/consent-manager/provider.tsx"
136
- 'use client';
137
-
138
- import type { ReactNode } from 'react';
139
- import {
140
- ConsentManagerProvider,
141
- policyPackPresets,
142
- } from '@c15t/nextjs';
143
-
144
- export function ConsentManager({ children }: { children: ReactNode }) {
145
- return (
146
- <ConsentManagerProvider
147
- options={{
148
- mode: 'offline',
149
- offlinePolicy: {
150
- policyPacks: [
151
- policyPackPresets.californiaOptOut(),
152
- policyPackPresets.europeOptIn(),
153
- policyPackPresets.worldNoBanner(),
154
- ],
155
- },
156
- overrides: {
157
- country: 'GB',
158
- },
159
- }}
160
- >
161
- {children}
162
- </ConsentManagerProvider>
163
- );
164
- }
165
- ```
166
-
167
- ## Policy-Aware Banner Example
168
-
169
- ```tsx title="components/consent-manager/custom-banner.tsx"
170
- 'use client';
171
-
172
- import { useHeadlessConsentUI, useTranslations } from '@c15t/nextjs/headless';
173
-
174
- export function CustomConsentBanner() {
175
- const { banner, openDialog, performBannerAction } = useHeadlessConsentUI();
176
- const translations = useTranslations();
177
-
178
- function getActionLabel(action: (typeof banner.allowedActions)[number]) {
179
- switch (action) {
180
- case 'accept':
181
- return translations.common.acceptAll;
182
- case 'reject':
183
- return translations.common.rejectAll;
184
- case 'customize':
185
- return translations.common.customize;
186
- }
187
- }
188
-
189
- if (!banner.isVisible) return null;
190
-
191
- return (
192
- <aside className="rounded-xl border bg-white p-6 shadow-lg">
193
- <h2 className="text-lg font-semibold">{translations.cookieBanner.title}</h2>
194
- <p className="mt-2 text-sm text-gray-600">
195
- {translations.cookieBanner.description}
196
- </p>
197
-
198
- <div className="mt-4 space-y-2">
199
- {banner.actionGroups.map((group, index) => (
200
- <div key={`${group.join('-')}-${index}`} className="flex gap-2">
201
- {group.map((action) => (
202
- <button
203
- key={action}
204
- type="button"
205
- className={banner.primaryActions.includes(action) ? 'btn-primary' : 'btn-secondary'}
206
- onClick={() => {
207
- if (action === 'customize') {
208
- openDialog();
209
- return;
210
- }
211
- void performBannerAction(action);
212
- }}
213
- >
214
- {getActionLabel(action)}
215
- </button>
216
- ))}
217
- </div>
218
- ))}
219
- </div>
220
- </aside>
221
- );
222
- }
223
- ```
224
-
225
- ## Category List That Respects the Resolved Policy
226
-
227
- ```tsx title="components/consent-manager/custom-dialog.tsx"
228
- 'use client';
229
-
230
- import {
231
- useConsentManager,
232
- useHeadlessConsentUI,
233
- useTranslations,
234
- } from '@c15t/nextjs/headless';
235
-
236
- export function CustomConsentDialog() {
237
- const { dialog, performDialogAction, saveCustomPreferences } = useHeadlessConsentUI();
238
- const {
239
- consentTypes,
240
- consentCategories,
241
- consents,
242
- selectedConsents,
243
- setSelectedConsent,
244
- } = useConsentManager();
245
- const translations = useTranslations();
246
-
247
- function getActionLabel(action: (typeof dialog.allowedActions)[number]) {
248
- switch (action) {
249
- case 'accept':
250
- return translations.common.acceptAll;
251
- case 'reject':
252
- return translations.common.rejectAll;
253
- case 'customize':
254
- return translations.common.save;
255
- }
256
- }
257
-
258
- if (!dialog.isVisible) return null;
259
-
260
- const displayedTypes = consentTypes.filter(
261
- (type) => type.display && consentCategories.includes(type.name)
262
- );
263
-
264
- return (
265
- <section className="rounded-xl border bg-white p-6 shadow-xl">
266
- <h2 className="text-lg font-semibold">
267
- {translations.consentManagerDialog.title}
268
- </h2>
269
-
270
- <div className="mt-4 space-y-3">
271
- {displayedTypes.map((type) => (
272
- <label key={type.name} className="flex items-start justify-between gap-4">
273
- <div>
274
- <p className="font-medium">
275
- {translations.consentTypes[type.name]?.title ?? type.name}
276
- </p>
277
- <p className="text-sm text-gray-600">{type.description}</p>
278
- </div>
279
- <input
280
- type="checkbox"
281
- checked={selectedConsents[type.name] ?? consents[type.name] ?? false}
282
- disabled={type.disabled}
283
- onChange={(event) =>
284
- setSelectedConsent(type.name, event.target.checked)
285
- }
286
- />
287
- </label>
288
- ))}
289
- </div>
290
-
291
- <div className="mt-4 space-y-2">
292
- {dialog.actionGroups.map((group, index) => (
293
- <div key={`${group.join('-')}-${index}`} className="flex gap-2">
294
- {group.map((action) => (
295
- <button
296
- key={action}
297
- type="button"
298
- onClick={() => {
299
- if (action === 'customize') {
300
- void saveCustomPreferences();
301
- return;
302
- }
303
- void performDialogAction(action);
304
- }}
305
- >
306
- {getActionLabel(action)}
307
- </button>
308
- ))}
309
- </div>
310
- ))}
311
- </div>
312
- </section>
313
- );
314
- }
315
- ```
316
-
317
- ## What Headless Is Not For
318
-
319
- Headless mode is not the recommended path for:
320
-
321
- * changing the banner footer background
322
- * rounding the stock banner card
323
- * restyling stock banner or dialog buttons
324
- * changing consent copy
325
-
326
- Those should stay in the pre-built stack with tokens, slots, `theme.consentActions`, and provider `i18n`.
327
-
328
- ## What a Policy-Aware Headless Component Should Respect
329
-
330
- When you build custom banner or dialog components, make sure they use:
331
-
332
- * `activeUI` or `banner.isVisible` / `dialog.isVisible` for visibility
333
- * `allowedActions`, `actionGroups`, and `primaryActions` instead of hard-coding buttons
334
- * `primaryActions` for visual emphasis
335
- * `consentCategories` when deciding which category toggles to render
336
- * `policyDecision` when you want to debug why a specific UI state was chosen
337
-
338
- If you ignore those values, your custom UI can drift away from the resolved policy pack even though the underlying consent engine is configured correctly.
339
-
340
- ## Test Custom UI Against the Resolved Policy
341
-
342
- ```ts
343
- import {
344
- getEffectivePolicy,
345
- type PolicyUIState,
346
- validateUIAgainstPolicy,
347
- } from 'c15t';
348
-
349
- const policy = getEffectivePolicy(initData);
350
-
351
- const dialogState: PolicyUIState = {
352
- mode: 'dialog',
353
- actions: ['accept', 'reject', 'customize'],
354
- layout: 'split',
355
- uiProfile: 'compact',
356
- scrollLock: true,
357
- };
358
-
359
- const issues = validateUIAgainstPolicy({
360
- policy,
361
- state: dialogState,
362
- });
363
-
364
- expect(issues).toEqual([]);
365
- ```
366
-
367
- ## Validation and Testing
368
-
369
- If you are building a reusable headless component library, validate your rendered UI against the resolved runtime policy in tests.
370
-
371
- The core package exposes:
372
-
373
- * `getEffectivePolicy(initData)` to read the resolved policy from `/init`
374
- * `validateUIAgainstPolicy({ policy, state })` to detect mismatches such as wrong actions, layout, or mode
375
-
376
- This is useful when your design system renders custom button arrangements and you want tests to catch policy drift early.
377
-
378
- > ℹ️ **Info:**
379
- > Pair this with Policy Packs when you want to exercise multiple regional UI states locally before wiring a live backend.
@@ -1,186 +0,0 @@
1
- ---
2
- title: Callbacks
3
- description: React to consent lifecycle events - initialization, consent
4
- changes, errors, and revocation reloads.
5
- group: frameworks
6
- ---
7
- Callbacks let you run custom code at key points in the consent lifecycle. Define them in the provider or runtime `callbacks` option, or register them dynamically after initialization.
8
-
9
- For analytics SDKs and other change-only integrations, prefer `subscribeToConsentChanges()` or `onConsentChanged`. Use `onConsentSet` when you want the broader lifecycle signal, including initialization, automatic defaults, and replay-aware registration.
10
-
11
- > ℹ️ **Info:**
12
- > subscribeToConsentChanges() is the recommended API for analytics SDKs and consent-mode integrations. It only emits future saves that actually changed persisted preferences.
13
-
14
- ## Configuration
15
-
16
- ```tsx
17
- import { type ReactNode } from 'react';
18
- import { ConsentManagerProvider } from '@c15t/nextjs';
19
-
20
- export function ConsentManager({ children }: { children: ReactNode }) {
21
- return (
22
- <ConsentManagerProvider
23
- options={{
24
- mode: 'hosted',
25
- backendURL: '/api/c15t',
26
- callbacks: {
27
- onBannerFetched: ({ jurisdiction, location, translations }) => {
28
- console.log('Jurisdiction:', jurisdiction);
29
- console.log('Country:', location.countryCode);
30
- console.log('Language:', translations.language);
31
- },
32
- onConsentSet: ({ preferences }) => {
33
- console.log('Consent lifecycle event:', preferences);
34
- },
35
- onConsentChanged: ({ allowedCategories, deniedCategories }) => {
36
- analytics.syncConsent({ allowedCategories, deniedCategories });
37
- },
38
- onError: ({ error }) => {
39
- errorReporter.captureMessage(error);
40
- },
41
- onBeforeConsentRevocationReload: ({ preferences }) => {
42
- // Flush pending analytics before page reloads
43
- analytics.flush();
44
- },
45
- },
46
- }}
47
- >
48
- {children}
49
- </ConsentManagerProvider>
50
- );
51
- }
52
- ```
53
-
54
- ## Choose the Right Surface
55
-
56
- |Surface|Replays when registered late?|Fires on init / hydration / auto-grants?|Best for|
57
- |--|--|--|--|
58
- |`onBannerFetched`|Yes, via `setCallback('onBannerFetched', ...)` after init|Yes|Logging resolved policy, location, and translations|
59
- |`onConsentSet`|Yes, via `setCallback('onConsentSet', ...)`|Yes|Broad lifecycle hooks, debugging, and integrations that want the latest full state regardless of how it was reached|
60
- |`onConsentChanged`|No|No|Declarative change-only integrations|
61
- |`subscribeToConsentChanges()`|No|No|Canonical change-only subscriptions after mount|
62
-
63
- > ℹ️ **Info:**
64
- > Script.onConsentChange is a script-scoped lifecycle hook. It is not the global consent change API for analytics SDKs or other app-wide integrations.
65
-
66
- ## Available Callbacks
67
-
68
- ### `onBannerFetched`
69
-
70
- Called when the consent banner data is fetched from the backend (or loaded from SSR data). The payload includes jurisdiction info, location data, and resolved translations.
71
-
72
- ```tsx
73
- onBannerFetched: ({ jurisdiction, location, translations }) => {
74
- // jurisdiction: 'GDPR' | 'CCPA' | { code: 'GDPR', message: '...' } | ...
75
- // location: { countryCode: 'DE', regionCode: 'BY' }
76
- // translations: { language: 'de', translations: {...} }
77
- }
78
- ```
79
-
80
- ### `onConsentSet`
81
-
82
- Called whenever c15t broadly settles consent state: store initialization, automatic defaults during init, explicit saves, and replay via `setCallback('onConsentSet', ...)`.
83
-
84
- ```tsx
85
- onConsentSet: ({ preferences }) => {
86
- // preferences: { necessary: true, measurement: true, marketing: false, ... }
87
- console.log('Latest consent state:', preferences);
88
- }
89
- ```
90
-
91
- ### `onConsentChanged`
92
-
93
- Called only after an explicit `saveConsents()` or `setConsent()` that actually changes the saved consent state. It never fires on store creation, hydration, automatic grants, unchanged saves, or `setCallback('onConsentChanged', ...)`.
94
-
95
- ```tsx
96
- onConsentChanged: ({
97
- preferences,
98
- previousPreferences,
99
- allowedCategories,
100
- deniedCategories,
101
- previousAllowedCategories,
102
- previousDeniedCategories,
103
- }) => {
104
- analytics.syncConsent({
105
- allowedCategories,
106
- deniedCategories,
107
- previousAllowedCategories,
108
- previousDeniedCategories,
109
- });
110
- }
111
- ```
112
-
113
- ### `onError`
114
-
115
- Called when an error occurs during consent operations (e.g., API request failure). If no `onError` callback is provided, errors are logged to `console.error`.
116
-
117
- ```tsx
118
- onError: ({ error }) => {
119
- // error: string describing what went wrong
120
- Sentry.captureMessage(`Consent error: ${error}`);
121
- }
122
- ```
123
-
124
- ### `onBeforeConsentRevocationReload`
125
-
126
- Called synchronously before the page reloads due to consent revocation. This is your last chance to run cleanup before the reload. Keep this callback fast - avoid async operations.
127
-
128
- ```tsx
129
- onBeforeConsentRevocationReload: ({ preferences }) => {
130
- // Flush any pending data
131
- navigator.sendBeacon('/api/flush', JSON.stringify({ session: sessionId }));
132
- }
133
- ```
134
-
135
- ## Change-Only Subscriptions
136
-
137
- Use `subscribeToConsentChanges()` when you want a stable listener for real preference changes after mount:
138
-
139
- ```tsx
140
- import { useEffect } from 'react';
141
- import { useConsentManager } from '@c15t/nextjs';
142
-
143
- function ConsentAnalytics() {
144
- const { subscribeToConsentChanges } = useConsentManager();
145
-
146
- useEffect(() => {
147
- return subscribeToConsentChanges(({ allowedCategories, deniedCategories }) => {
148
- analytics.syncConsent({ allowedCategories, deniedCategories });
149
- });
150
- }, [subscribeToConsentChanges]);
151
-
152
- return null;
153
- }
154
- ```
155
-
156
- ## Runtime Callback Registration
157
-
158
- Register or update callbacks at runtime using `setCallback()`:
159
-
160
- ```tsx
161
- import { useEffect } from 'react';
162
- import { useConsentManager } from '@c15t/nextjs';
163
-
164
- function ConsentAnalytics() {
165
- const { setCallback } = useConsentManager();
166
-
167
- useEffect(() => {
168
- setCallback('onBannerFetched', ({ jurisdiction, location }) => {
169
- console.log('Resolved init data:', { jurisdiction, location });
170
- });
171
-
172
- setCallback('onConsentSet', ({ preferences }) => {
173
- console.log('Broad consent lifecycle event:', preferences);
174
- });
175
-
176
- return () => {
177
- setCallback('onBannerFetched', undefined);
178
- setCallback('onConsentSet', undefined);
179
- };
180
- }, [setCallback]);
181
-
182
- return null;
183
- }
184
- ```
185
-
186
- `setCallback('onConsentSet', ...)` immediately replays the current consent state. For change-only logic, prefer `subscribeToConsentChanges()` or `onConsentChanged`.