@c15t/nextjs 2.2.1 → 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
@@ -0,0 +1,334 @@
1
+ ---
2
+ title: Upgrade to v3 policies
3
+ description: Migrate policy configuration, consent records, callbacks, and
4
+ custom transports to the v3 policy system.
5
+ group: reference
6
+ ---
7
+
8
+ ## Start with a policy rule
9
+
10
+ ```ts
11
+ import { policyRulePresets } from '@c15t/schema';
12
+ import { offline } from '@c15t/react';
13
+
14
+ const mode = offline({
15
+ policyRules: [policyRulePresets.europeOptIn()],
16
+ });
17
+ ```
18
+
19
+ Pass `mode` to `ConsentProvider`. For a backend integration, configure
20
+ `policyRules` on the backend and use `hosted({ url })` in the provider. Follow the
21
+ [React quickstart](https://c15t.com/docs/frameworks/react/quickstart),
22
+ [Next.js quickstart](./frameworks/next/quickstart.md), or
23
+ [JavaScript quickstart](https://c15t.com/docs/frameworks/javascript/quickstart) for a complete
24
+ integration.
25
+
26
+ The CLI no longer offers `offline-add-policy-packs`, which generated v2
27
+ configuration. For offline integrations, configure `offline({ policyRules })`
28
+ as shown above.
29
+
30
+ Replace legacy `policyPacks` and nested `consent` configuration with rules:
31
+
32
+ ```ts
33
+ import type { PolicyRule } from '@c15t/schema';
34
+
35
+ const policyRules = [
36
+ {
37
+ id: 'default-opt-in',
38
+ match: { fallback: true },
39
+ model: 'opt-in',
40
+ prompt: 'choice',
41
+ categories: ['measurement', 'marketing'],
42
+ scopeMode: 'strict',
43
+ },
44
+ ] satisfies PolicyRule[];
45
+ ```
46
+
47
+ `model` controls permission defaults. `prompt` controls whether the visitor must
48
+ make a choice, dismiss a notice, or see no prompt. Opt-in and IAB models require
49
+ `prompt: 'choice'`. Opt-out supports `choice`, `notice`, and `none`. The `none`
50
+ model permits optional categories in scope, allows only `prompt: 'none'`, owes
51
+ no rights, and renders no consent UI. v2's `none` model maps to it directly.
52
+
53
+ When `categories` selects only some optional categories, you must set
54
+ `scopeMode` explicitly. A strict scope blocks categories outside the rule. A permissive scope allows
55
+ those categories unless another restriction applies. An omitted scope, `['*']`,
56
+ or a list containing only `necessary` expands to the default optional categories.
57
+ `necessary` is always permitted.
58
+
59
+ `offline()` without `policyRules` resolves `recommendedPolicyRules()`, whose
60
+ Europe rule also matches a visitor with no country. A known US country with a
61
+ missing state uses US opt-out with GPC and persistent preferences; a missing
62
+ Canadian province stays strict opt-in. The last rule is `none` for other known
63
+ unmatched locations. Resolution exposes `matched`, `no-match`,
64
+ `unconfigured`, or `failed`; while nothing has matched, optional categories stay
65
+ denied and no consent surface renders. A missing or malformed policy never grants
66
+ optional permissions by itself.
67
+
68
+ ## Read the state you need
69
+
70
+ | Purpose | Snapshot field | React hook |
71
+ | ----------------------------------------------- | ---------------------- | --------------------------------------- |
72
+ | Gate scripts and optional features | `effectivePermissions` | `useConsent(category)`, `useConsents()` |
73
+ | Inspect recorded choices and confirmation times | `explicitChoice` | `useExplicitChoice()` |
74
+ | Decide whether to prompt | `promptRequirement` | `usePromptRequirement()` |
75
+ | Inspect the resolved rule | `policyRule` | `usePolicyRule()` |
76
+ | Inspect matching or failure | `resolution` | `usePolicyResolution()` |
77
+
78
+ An effective permission is not evidence of a grant. Under an opt-out rule it can
79
+ be true before a visitor acts. A notice dismissal updates `noticeDismissal` and
80
+ does not record consent. Global Privacy Control updates privacy signals and
81
+ configured opt-out directives without turning a browser signal into a choice.
82
+
83
+ ## Replace choice callbacks
84
+
85
+ ```tsx
86
+ <ConsentProvider
87
+ options={{
88
+ mode,
89
+ callbacks: {
90
+ onChoiceRecorded(event) {
91
+ console.log('Visitor recorded a choice', event);
92
+ },
93
+ onPermissionsChanged(event) {
94
+ console.log('Effective permissions changed', event);
95
+ },
96
+ },
97
+ }}
98
+ >
99
+ {children}
100
+ </ConsentProvider>
101
+ ```
102
+
103
+ Replace `onConsentSet` with `onChoiceRecorded` for explicit accept, reject, or
104
+ save actions. Use `onPermissionsChanged` for permission changes caused by a
105
+ choice, expiry, policy update, or privacy signal. Provider callbacks no longer
106
+ include `onBannerFetched`; use the resolved policy and pending state to render
107
+ loading UI.
108
+
109
+ Only `kernel.commands.save()` creates an explicit choice. `save('all')` accepts,
110
+ `save('none')` rejects, and `save({ marketing: false })` confirms only marketing.
111
+ A partial save keeps the other categories' confirmation times. Use
112
+ `commands.dismissNotice()` for the notice action and `kernel.hydrate()` to apply
113
+ validated records without recording an action.
114
+
115
+ ## Keep existing storage
116
+
117
+ The reader accepts valid v2 storage and translates it into per-category receipts.
118
+ It does not rewrite storage on startup. Existing denials continue to restrict
119
+ permissions; expired or incompatible positive receipts cannot restore grants.
120
+ The next explicit action writes the v3 record. Notice dismissal and privacy
121
+ opt-out directives have separate records.
122
+
123
+ Pass server-prepared `initialRecords`, `initialPolicyResolution`, and `now`
124
+ through the adapter's prefetch configuration. Do not rebuild consent from a
125
+ boolean `hasConsented` or copy effective permissions into explicit choice records.
126
+ Pending prefetch results cannot restore records after a clear or overwrite a
127
+ newer choice.
128
+
129
+ ## Update custom transports and backend clients
130
+
131
+ Use `KernelTransport` from `@c15t/core/transports`. Init returns a versioned
132
+ `policyResolution` with a matched rule and fingerprints, or an explicit
133
+ non-matched outcome. Use `mapInitOutputToInitResponse` for an HTTP init payload.
134
+ Do not return legacy `policy` or `policyDecision` fields.
135
+
136
+ Save requests contain the explicit choice and the categories confirmed by this
137
+ action. Return a `SaveResult` with `ok`; optional identity and record methods
138
+ must preserve the same receipt format. Forward the policy contract headers when
139
+ implementing an HTTP proxy. Keep client, schema, backend, and framework adapters
140
+ on compatible v3 versions.
141
+
142
+ Use the supplied hosted or manifest transports to capture policy evidence for
143
+ saves and retries. They preserve the action's policy, location, language, and
144
+ privacy signal inputs. A stale policy assertion is an error; it must not silently
145
+ save under a different rule. See the
146
+ [backend endpoint reference](https://c15t.com/docs/self-host/api/endpoints) for wire fields.
147
+
148
+ ## Shared runtime ownership
149
+
150
+ Astro and SvelteKit can create a runtime outside a component tree with
151
+ `createConsentRuntime` from `@c15t/core/runtime`. Pass that runtime to a
152
+ framework provider with its `runtime` prop. The owner calls `start()` after
153
+ mount and `dispose()` when the page no longer needs it. Borrowing providers
154
+ subscribe to the kernel and render UI without initializing or disposing it.
155
+
156
+ Runtime construction preserves the prepared server snapshot. Storage reads,
157
+ privacy-signal activation and script loading begin on `start()`. Pass server
158
+ records through `prefetch.initialRecords` with their evaluation time to keep
159
+ the first browser render consistent with the server. `runtime.clearRecords()`
160
+ clears both persisted and in-memory records.
161
+
162
+ Runtime callbacks use `onChoiceRecorded`, `onPermissionsChanged` and
163
+ `onError`. Hydrating records does not report a new visitor choice.
164
+
165
+ Astro's serializable offline descriptor accepts `policyRules`, just like the
166
+ other adapters' offline factories. Configure a preset or explicit rules;
167
+ omitting them keeps the conservative fallback.
168
+
169
+ ## Presentation corrections
170
+
171
+ Use `blocking` for backdrop, scroll locking and focus trapping together.
172
+ Choice banners default to non-blocking, choice walls always block, and notices
173
+ never block. A notice configured as a wall falls back to a floating card.
174
+ Preferences remain centered; `variant` and `position` apply only to prompts.
175
+
176
+ Replace reads of `uncoveredRights` with `preferenceControls`. The latter is a
177
+ list of additional preferences buttons recommended for the stock UI. It does
178
+ not verify disclosure or persistent access to policy rights.
179
+
180
+ Notice acknowledgement uses `common.acknowledge`, falling back to
181
+ `common.dismiss` for older translation bundles. It does not record a choice.
182
+
183
+ ## Astro notice acknowledgement
184
+
185
+ ```astro
186
+ ---
187
+ import ConsentBanner from '@c15t/astro/components/consent-banner.astro';
188
+ ---
189
+
190
+ <ConsentBanner dismissButtonText="Got it" />
191
+ ```
192
+
193
+ Astro's `ConsentBanner` accepts `dismissButtonText` as an optional string. It
194
+ labels the notice acknowledgement button, which defaults to
195
+ `common.acknowledge` and falls back to `common.dismiss` in older translation
196
+ bundles. Acknowledging a notice preserves the visitor's consent choices.
197
+
198
+ ## IAB blocking behavior
199
+
200
+ IAB banners and dialogs use the same `presentation.prompt.blocking` and
201
+ `presentation.preferences.blocking` settings as the other consent components.
202
+ A blocking surface shows a backdrop, traps focus and locks page scrolling.
203
+ Explicit `blocking` values take precedence over deprecated `scrollLock` and
204
+ `trapFocus` options. IAB components stay hidden without a matched policy,
205
+ including when a dialog receives `open={true}`.
206
+
207
+ The React consent dialog still blocks pointer interaction with the page when
208
+ `blocking` is true and you hide its backdrop with `overlay={false}`.
209
+
210
+ Legacy prompt options now control the whole blocking behavior. For example,
211
+ `scrollLock: true` alone enables a backdrop and focus trapping as well as
212
+ scroll locking. Set `blocking: false` explicitly for a non-modal banner.
213
+
214
+ Custom dialog backdrops follow the same rule. A dialog resolved as non-blocking
215
+ omits both the default backdrop and a supplied `overlay`. To retain a custom
216
+ backdrop when migrating from `trapFocus: false`, set
217
+ `presentation.preferences.blocking: true` and keep your `overlay` prop.
218
+
219
+ ## Next.js and TanStack Start renames
220
+
221
+ These names changed without deprecated aliases. `defineConsentConfig`,
222
+ `ConsentConfig` (the URL config) and `ConsentProvider` from `@c15t/react` are
223
+ unchanged.
224
+
225
+ Next.js (`c15t/next`, `c15t/next/server`, `c15t/next/pages`):
226
+
227
+ | v2 | v3 |
228
+ | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
229
+ | `ConsentBoundary` | `ConsentRoot` |
230
+ | `ConsentBoundaryProps` | `ConsentRootProps` |
231
+ | `config={initialConsent}` (the visitor's resolved state) | `state={...}` |
232
+ | `consent={consentConfig}` (the `defineConsentConfig` result) | `config={consentConfig}` |
233
+ | `prefetchInitialConsent(options)` | `resolveConsent(options)` with `config` or `backendURL` |
234
+ | `readInitialConsentConfig(options)` | `resolveConsent(options)` without `config` or `backendURL` |
235
+ | `InitialConsentConfig`, and `KernelConfig` where it named the returned value | `ConsentState` |
236
+ | `PrefetchInitialConsentOptions` | `ResolveConsentOptions` |
237
+ | `ReadInitialConsentConfigOptions` | `ConsentRequestOptions` |
238
+ | Pages Router `readInitialConsentConfig(req, opts)` and `prefetchInitialConsent({ req, ... })` | `resolveConsent({ req, ... })` |
239
+
240
+ TanStack Start (`c15t/tanstack-start`, `c15t/tanstack-start/server`):
241
+
242
+ | v2 | v3 |
243
+ | ------------------------------------- | ---------------------------------------------- |
244
+ | `ConsentBoundary` | `ConsentRoot` |
245
+ | `config={config}` | `state={state}` |
246
+ | `ConsentConfig` (the returned state) | `ConsentState` |
247
+ | `prefetchInitialConsent` | `resolveConsent(options)` with `backendURL` |
248
+ | `readInitialConsentConfig` | `resolveConsent(options)` without `backendURL` |
249
+ | `createConsentConfigHandler(options)` | `createConsentStateHandler(options)` |
250
+ | `mergeInitIntoConsentConfig` | `mergeInitIntoConsentState` |
251
+
252
+ `consentLoaderOptions`, `backendURL`, `initRoute` and `DEFAULT_INIT_ROUTE` are
253
+ unchanged.
254
+
255
+ ## Next.js RSC banner removal
256
+
257
+ The `@c15t/nextjs/rsc` entry (`c15t/next/rsc`) and its `RscConsentBanner`,
258
+ `RscBannerGate` and `RscBannerActions` exports are gone. Imports of that
259
+ entry now fail to resolve. Use the regular banner for server rendering.
260
+ The retained benchmark results showed slightly faster banner visibility and
261
+ interaction for the experimental shell, so this removal should not be read
262
+ as a performance improvement.
263
+
264
+ Replace `<RscConsentBanner config={config} />` with `<ConsentBanner />` inside
265
+ the same `ConsentRoot`, importing it from `c15t/next` when you use the
266
+ umbrella package or from `@c15t/nextjs` when you depend on the scoped package
267
+ directly. With an awaited `resolveConsent` result, the server renders
268
+ the banner into the response, which is what the RSC variant was for. Move
269
+ `presentation` to `options.presentation` on `ConsentRoot`.
270
+
271
+ Two defaults differ from the removed shell. It rendered no branding link, so
272
+ pass `hideBranding` to keep that. It also styled only its root and action row
273
+ (through the stock `ConsentBanner.Root` and `PolicyActions`) and left the
274
+ card, title, description, footer and buttons without base classes, whereas
275
+ `ConsentBanner` merges the stock styles into every part. If that changes a
276
+ custom layout, do not reach for `noStyle` on the whole banner, which also
277
+ drops the root positioning and action-row layout the old shell kept; compose
278
+ the compound parts instead and pass `noStyle` only to the parts you styled
279
+ yourself.
280
+
281
+ `ConsentBanner` has no `classNames` or `children` props. Move each legacy
282
+ `classNames` key to the `ConsentRoot` `options.components` slots, which take
283
+ `{ className }`:
284
+
285
+ | `classNames` key | Replacement |
286
+ | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
287
+ | `root`, `card`, `title`, `footer` | `components.banner.root`, `.card`, `.title`, `.footer` |
288
+ | `description` | `components.description.banner` |
289
+ | `rights`, `rightLink` | `components.banner.rights`, `components.banner.rightLink` |
290
+ | `acceptButton`, `rejectButton`, `customizeButton`, `dismissButton` | No per-button slot. Use `components.button.primary` / `.secondary` for shared styles, or target `[data-action="accept"]`, `[data-action="reject"]`, `[data-action="customize"]` and `[data-action="dismiss"]` in CSS. |
291
+
292
+ Custom `children`, and any direct use of `RscBannerGate` or
293
+ `RscBannerActions`, map to the compound parts of `ConsentBanner` in a Client
294
+ Component. `RscBannerGate` becomes `ConsentBanner.Root`, which mounts only
295
+ while the policy owes a prompt and reopens on expiry. Remove the gate
296
+ `prompt` and `model` props; the root derives `data-prompt` and `data-model`
297
+ from the `ConsentRoot` policy. Rename `title` to `aria-label` to preserve its
298
+ accessible label. Keep `children`, `className`, `variant`, `position` and
299
+ `blocking` on the root. Add `disableAnimation` and `trapFocus={false}` to
300
+ retain the old gate defaults.
301
+
302
+ `RscBannerActions`
303
+ becomes `ConsentBanner.PolicyActions`, which renders the rights links and the
304
+ action row the policy requires; `ConsentBanner.Rights`, `RightLink`, `Footer`,
305
+ `FooterSubGroup` and the four buttons are available for finer control.
306
+ `PolicyActions` takes none of the old `acceptLabel`, `rejectLabel`,
307
+ `customizeLabel`, `dismissLabel`, `rightLabels` or `classNames` props: set
308
+ labels through the provider `i18n` translation overrides (`common.acceptAll`,
309
+ `common.rejectAll`, `common.customize`, `common.acknowledge`, `rights.*`), or
310
+ render `ConsentBanner.AcceptButton` and the other buttons with your own
311
+ children; `renderAction` on `PolicyActions` replaces one button; classes move
312
+ to the `components` slots listed in the table. Place the former `children`
313
+ inside `ConsentBanner.Card`:
314
+
315
+ ```tsx title="components/banner.tsx"
316
+ 'use client';
317
+
318
+ import { ConsentBanner } from 'c15t/next';
319
+
320
+ export function Banner() {
321
+ return (
322
+ <ConsentBanner.Root>
323
+ <ConsentBanner.Card>
324
+ <ConsentBanner.Header>
325
+ <ConsentBanner.Title />
326
+ <ConsentBanner.Description />
327
+ </ConsentBanner.Header>
328
+ <a href="/privacy">Privacy policy</a>
329
+ <ConsentBanner.PolicyActions />
330
+ </ConsentBanner.Card>
331
+ </ConsentBanner.Root>
332
+ );
333
+ }
334
+ ```
package/iab/styles.css CHANGED
@@ -1 +1 @@
1
- @import "../dist/iab/styles.css";
1
+ @import '../dist/iab/styles.css';
@@ -1 +1 @@
1
- @import "../dist/iab/styles.tw3.css";
1
+ @import '../dist/iab/styles.tw3.css';
package/package.json CHANGED
@@ -1,55 +1,70 @@
1
1
  {
2
2
  "name": "@c15t/nextjs",
3
- "version": "2.2.1",
3
+ "version": "3.0.0-alpha.0",
4
4
  "description": "Next.js cookie banner and consent management platform for App Router, Pages Router, SSR, IAB TCF, and Consent Mode.",
5
5
  "keywords": [
6
- "nextjs",
7
- "next",
8
- "next-cookie-banner",
9
- "nextjs-consent",
10
- "next-cmp",
11
- "consent",
12
- "privacy",
13
- "gdpr",
6
+ "app-router",
14
7
  "ccpa",
15
- "lgpd",
16
- "tcf",
17
- "iab",
18
8
  "cmp",
19
- "react",
20
- "headless",
21
- "typescript",
9
+ "consent",
10
+ "consent-banner",
11
+ "consent-management",
12
+ "consent-management-platform",
13
+ "consent-mode-v2",
22
14
  "cookie-banner",
23
15
  "cookie-consent",
24
- "consent-management-platform",
25
- "consent-management",
26
- "consent-banner",
27
- "preference-center",
28
- "app-router",
16
+ "gdpr",
17
+ "google-consent-mode",
18
+ "headless",
19
+ "iab",
20
+ "lgpd",
21
+ "next",
22
+ "next-cmp",
23
+ "next-cookie-banner",
24
+ "nextjs",
25
+ "nextjs-cmp",
26
+ "nextjs-consent",
29
27
  "pages-router",
30
- "server-components",
28
+ "preference-center",
29
+ "privacy",
30
+ "react",
31
31
  "rsc",
32
- "ssr",
32
+ "server-components",
33
33
  "server-side-rendering",
34
+ "ssr",
35
+ "tcf",
34
36
  "tracking-consent",
35
- "google-consent-mode",
36
- "consent-mode-v2",
37
- "nextjs-cmp"
37
+ "typescript"
38
38
  ],
39
39
  "homepage": "https://c15t.com/docs/frameworks/next/quickstart",
40
40
  "bugs": {
41
41
  "url": "https://github.com/c15t/c15t/issues"
42
42
  },
43
+ "license": "Apache-2.0",
43
44
  "repository": {
44
45
  "type": "git",
45
46
  "url": "https://github.com/c15t/c15t.git",
46
47
  "directory": "packages/nextjs"
47
48
  },
48
- "license": "Apache-2.0",
49
+ "files": [
50
+ "dist",
51
+ "dist-types",
52
+ "AGENTS.md",
53
+ "docs",
54
+ "styles.css",
55
+ "styles.tw3.css",
56
+ "iab",
57
+ "src/styles.css",
58
+ "src/styles.tw3.css",
59
+ "src/iab/styles.css",
60
+ "src/iab/styles.tw3.css"
61
+ ],
62
+ "type": "module",
49
63
  "sideEffects": [
50
64
  "**/*.css"
51
65
  ],
52
- "type": "module",
66
+ "main": "./dist/index.js",
67
+ "types": "./dist-types/index.d.ts",
53
68
  "exports": {
54
69
  "./styles.css": "./dist/styles.css",
55
70
  "./styles.tw3.css": "./dist/styles.tw3.css",
@@ -58,70 +73,96 @@
58
73
  "./headless": {
59
74
  "types": "./dist-types/headless.d.ts",
60
75
  "import": "./dist/headless.js",
61
- "require": "./dist/headless.cjs"
76
+ "default": "./dist/headless.js"
62
77
  },
63
78
  "./components/consent-dialog-link": {
64
79
  "types": "./dist-types/index.d.ts",
65
- "import": "./client/components/consent-dialog-link.js",
66
- "require": "./dist/index.cjs"
67
- },
68
- "./components/integrations": {
69
- "types": "./dist-types/components/integrations/index.d.ts",
70
- "import": "./client/components/integrations.js",
71
- "require": "./dist/components/integrations/index.cjs"
80
+ "import": "./dist/index.js",
81
+ "default": "./dist/index.js"
72
82
  },
73
83
  ".": {
74
84
  "types": "./dist-types/index.d.ts",
75
85
  "import": "./dist/index.js",
76
- "require": "./dist/index.cjs"
86
+ "default": "./dist/index.js"
87
+ },
88
+ "./server": {
89
+ "types": "./dist-types/server.d.ts",
90
+ "import": "./dist/server.js",
91
+ "default": "./dist/server.js"
92
+ },
93
+ "./api": {
94
+ "types": "./dist-types/api.d.ts",
95
+ "import": "./dist/api.js",
96
+ "default": "./dist/api.js"
97
+ },
98
+ "./middleware": {
99
+ "types": "./dist-types/middleware.d.ts",
100
+ "import": "./dist/middleware.js",
101
+ "default": "./dist/middleware.js"
102
+ },
103
+ "./proxy": {
104
+ "types": "./dist-types/proxy.d.ts",
105
+ "import": "./dist/proxy.js",
106
+ "default": "./dist/proxy.js"
107
+ },
108
+ "./pages": {
109
+ "types": "./dist-types/pages.d.ts",
110
+ "import": "./dist/pages.js",
111
+ "default": "./dist/pages.js"
112
+ },
113
+ "./static": {
114
+ "types": "./dist-types/static.d.ts",
115
+ "import": "./dist/static.js",
116
+ "default": "./dist/static.js"
117
+ },
118
+ "./devtools": {
119
+ "types": "./dist-types/devtools.d.ts",
120
+ "import": "./dist/devtools.js",
121
+ "default": "./dist/devtools.js"
77
122
  }
78
123
  },
79
- "main": "./dist/index.js",
80
- "module": "./dist/index.js",
81
- "types": "./dist-types/index.d.ts",
82
- "files": [
83
- "dist",
84
- "dist-types",
85
- "AGENTS.md",
86
- "docs",
87
- "client",
88
- "styles.css",
89
- "styles.tw3.css",
90
- "iab",
91
- "src/styles.css",
92
- "src/styles.tw3.css",
93
- "src/iab/styles.css",
94
- "src/iab/styles.tw3.css"
95
- ],
124
+ "publishConfig": {
125
+ "access": "public"
126
+ },
96
127
  "scripts": {
97
- "prebuild": "genversion --esm --semi src/version.ts",
98
- "build": "bun prebuild && rslib build && bun ../../scripts/normalize-dist-types.mjs && bun scripts/generate-distribution-css.ts && bun ../../scripts/generate-package-docs.ts @c15t/nextjs",
128
+ "build": "bun prebuild && rslib build && bun ../../scripts/normalize-dist-types.mjs && bun scripts/generate-distribution-css.ts && node ../shared/restore-client-directives.mjs src dist",
99
129
  "build:docs": "bun ../../scripts/generate-package-docs.ts @c15t/nextjs",
100
130
  "check-types": "bun prebuild && tsc --noEmit",
101
131
  "dev": "sh -c 'bun prebuild && rslib build --no-dts --no-clean && bun scripts/generate-distribution-css.ts && rslib build --watch --no-dts --no-clean'",
102
- "fmt": "bun biome format --write . && bun biome check --formatter-enabled=false --linter-enabled=false --write",
103
- "lint": "bun biome lint ./src",
104
- "prepack": "bun ../../scripts/verify-package-artifacts.ts",
132
+ "fmt": "bun oxfmt .",
133
+ "lint": "bun oxlint ./src",
134
+ "prebuild": "genversion --esm --semi src/version.ts",
135
+ "prepack": "bun run build:docs && bun ../../scripts/verify-package-artifacts.ts",
105
136
  "test": "bun prebuild && vitest run --passWithNoTests",
106
137
  "test:watch": "bun prebuild && vitest --passWithNoTests"
107
138
  },
108
139
  "dependencies": {
109
- "@c15t/react": "2.2.1",
110
- "@c15t/translations": "2.2.1",
111
- "c15t": "2.2.1"
140
+ "@c15t/core": "3.0.0-alpha.0",
141
+ "@c15t/react": "3.0.0-alpha.0",
142
+ "@c15t/schema": "3.0.0-alpha.0",
143
+ "@c15t/translations": "3.0.0-alpha.0"
112
144
  },
113
145
  "devDependencies": {
146
+ "@c15t/conformance": "0.0.1",
114
147
  "@c15t/typescript-config": "0.0.1",
115
148
  "@c15t/vitest-config": "1.0.0",
116
149
  "genversion": "3.2.0",
117
- "typescript": "7.0.1-rc"
150
+ "typescript": "7.0.2"
118
151
  },
119
152
  "peerDependencies": {
120
- "next": "^16.0.0 || ^15.0.0 || ^14.0.0 || ^13.0.0",
153
+ "next": "^15.0.0 || ^16.0.0",
121
154
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
122
155
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
123
156
  },
124
- "publishConfig": {
125
- "access": "public"
157
+ "peerDependenciesMeta": {
158
+ "next": {
159
+ "optional": true
160
+ },
161
+ "react": {
162
+ "optional": true
163
+ },
164
+ "react-dom": {
165
+ "optional": true
166
+ }
126
167
  }
127
168
  }
package/readme.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "title": "@c15t/nextjs: Next.js Integration",
3
3
  "description": "Next.js cookie banner and consent management platform for App Router, Pages Router, SSR, and headless consent flows.",
4
4
  "features": [
5
- "Works with Next.js 16, 15, 14, and 13",
5
+ "Works with Next.js 16 and 15",
6
6
  "Full 'use client' support for React Server Components",
7
7
  "Server-side rendering support for both app and pages routers",
8
8
  "Prebuilt and customizable cookie banner, consent dialog, and preference center UI",
@@ -14,9 +14,9 @@
14
14
  "Seamless consent storage and tracking"
15
15
  ],
16
16
  "prerequisites": [
17
- "Next.js 13.5.4 or later",
17
+ "Next.js 15.0.0 or later",
18
18
  "React 18 or later",
19
- "Node.js 18.17.0 or later",
19
+ "Node.js 20.19 or later",
20
20
  "A hosted [c15t instance](https://inth.com) (free sign-up), [self-hosted deployment](https://c15t.com/docs/self-host/quickstart), or offline mode for local-only storage"
21
21
  ],
22
22
  "manualInstallation": [
@@ -9,4 +9,4 @@
9
9
  * @import "@c15t/nextjs/styles.css";
10
10
  * @import "@c15t/nextjs/iab/styles.css";
11
11
  */
12
- @import "@c15t/react/iab/styles.css";
12
+ @import '@c15t/react/iab/styles.css';
@@ -11,4 +11,4 @@
11
11
  * @import "@c15t/nextjs/iab/styles.tw3.css";
12
12
  * @tailwind utilities;
13
13
  */
14
- @import "@c15t/react/iab/styles.tw3.css";
14
+ @import '@c15t/react/iab/styles.tw3.css';
package/src/styles.css CHANGED
@@ -7,4 +7,4 @@
7
7
  * Usage (app/globals.css):
8
8
  * @import "@c15t/nextjs/styles.css";
9
9
  */
10
- @import "@c15t/react/styles.css";
10
+ @import '@c15t/react/styles.css';
@@ -10,4 +10,4 @@
10
10
  * @import "@c15t/nextjs/styles.tw3.css";
11
11
  * @tailwind utilities;
12
12
  */
13
- @import "@c15t/react/styles.tw3.css";
13
+ @import '@c15t/react/styles.tw3.css';
package/styles.css CHANGED
@@ -1 +1 @@
1
- @import "./dist/styles.css";
1
+ @import './dist/styles.css';
package/styles.tw3.css CHANGED
@@ -1 +1 @@
1
- @import "./dist/styles.tw3.css";
1
+ @import './dist/styles.tw3.css';
@@ -1,3 +0,0 @@
1
- 'use client';
2
-
3
- export * from '@c15t/react/components/consent-dialog-link';
@@ -1,3 +0,0 @@
1
- 'use client';
2
-
3
- export * from '@c15t/react/components/integrations';
@@ -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__={144(_){_.exports=require("@c15t/react/components/integrations")}},__webpack_module_cache__={};function __webpack_require__(_){var e=__webpack_module_cache__[_];if(void 0!==e)return e.exports;var r=__webpack_module_cache__[_]={exports:{}};return __webpack_modules__[_](r,r.exports,__webpack_require__),r.exports}__webpack_require__.n=_=>{var e=_&&_.__esModule?()=>_.default:()=>_;return __webpack_require__.d(e,{a:e}),e},__webpack_require__.d=(_,e,r)=>{var a=(e,r)=>{for(var a in e)__webpack_require__.o(e,a)&&!__webpack_require__.o(_,a)&&Object.defineProperty(_,a,{enumerable:!0,[r]:e[a]})};a(e,"get"),a(r,"value")},__webpack_require__.o=(_,e)=>Object.prototype.hasOwnProperty.call(_,e),__webpack_require__.r=_=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(_,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(_,"__esModule",{value:!0})};var __webpack_exports__={};for(var __rspack_i in(()=>{__webpack_require__.r(__webpack_exports__);var _=__webpack_require__(144),e={};for(let r in _)"default"!==r&&(e[r]=()=>_[r]);__webpack_require__.d(__webpack_exports__,e)})(),__webpack_exports__)exports[__rspack_i]=__webpack_exports__[__rspack_i];Object.defineProperty(exports,"__esModule",{value:!0});
@@ -1 +0,0 @@
1
- export*from"@c15t/react/components/integrations";
package/dist/headless.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__={773(_){_.exports=require("@c15t/react/headless")}},__webpack_module_cache__={};function __webpack_require__(_){var e=__webpack_module_cache__[_];if(void 0!==e)return e.exports;var r=__webpack_module_cache__[_]={exports:{}};return __webpack_modules__[_](r,r.exports,__webpack_require__),r.exports}__webpack_require__.n=_=>{var e=_&&_.__esModule?()=>_.default:()=>_;return __webpack_require__.d(e,{a:e}),e},__webpack_require__.d=(_,e,r)=>{var a=(e,r)=>{for(var a in e)__webpack_require__.o(e,a)&&!__webpack_require__.o(_,a)&&Object.defineProperty(_,a,{enumerable:!0,[r]:e[a]})};a(e,"get"),a(r,"value")},__webpack_require__.o=(_,e)=>Object.prototype.hasOwnProperty.call(_,e),__webpack_require__.r=_=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(_,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(_,"__esModule",{value:!0})};var __webpack_exports__={};for(var __rspack_i in(()=>{__webpack_require__.r(__webpack_exports__);var _=__webpack_require__(773),e={};for(let r in _)"default"!==r&&(e[r]=()=>_[r]);__webpack_require__.d(__webpack_exports__,e)})(),__webpack_exports__)exports[__rspack_i]=__webpack_exports__[__rspack_i];Object.defineProperty(exports,"__esModule",{value:!0});