@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
@@ -1,235 +1,199 @@
1
1
  ---
2
2
  title: Optimization
3
- description: Improve c15t startup performance in Next.js with same-origin
4
- rewrites, static prefetching, and dynamic-route SSR.
3
+ description: Reuse cached policy data and optionally reduce browser connection
4
+ overhead without changing consent behavior.
5
5
  group: frameworks
6
6
  ---
7
- Use this guide when you care about banner visibility speed, route static-ness, and reducing backend round-trip cost.
8
7
 
9
- ## Start Here
8
+ ## Reuse cached policy data
10
9
 
11
- Apply the optimizations in this order:
10
+ The [App Router](./app-router.md) and
11
+ [Pages Router](./pages-router.md) guides use cached manifests
12
+ with Inth. A manifest contains public policy configuration that your app server
13
+ can reuse across requests. Each visitor's consent is still resolved separately.
12
14
 
13
- |Situation|Use|Why|
14
- |--|--|--|
15
- |Any production Next.js app|Same-origin `/api/c15t` rewrite|Lowers browser startup overhead and keeps the backend origin out of client config|
16
- |Static route, but banner speed matters|`C15tPrefetch`|Starts `/init` before hydration without making the route dynamic|
17
- |Dynamic route, or you want the fastest first banner|`fetchInitialData()`|Starts `/init` on the server and streams the result into the provider|
18
- |You want the simplest setup|Client-only init|No extra moving parts, but the banner appears later on cold loads|
15
+ A warm cache avoids repeated backend policy resolution. It does not eliminate
16
+ all requests. Your app may read its local manifest route, cold caches fetch
17
+ upstream data, and consent choices still reach Inth. IAB policies can also need
18
+ a Global Vendor List fetch.
19
19
 
20
- > ℹ️ **Info:**
21
- > C15tPrefetch is the only static-route prefetch step you need in @c15t/nextjs. Matching prefetched data is consumed automatically during first initialization.
22
- >
23
- > ℹ️ **Info:**
24
- > Prefetched or SSR data is reused only when the request context still matches at runtime. That includes the backend URL, credentials, overrides, and the browser's ambient GPC signal.
20
+ Server prefetch supplies the initial policy to the browser. For browser
21
+ initialization that needs request geography, you can add an
22
+ [optional local init route](./api-reference/data-fetching.md#do-i-need-the-local-init-route).
25
23
 
26
- In production benchmarks with a same-origin rewrite, prefetching strategies show measurable improvement over client-only init:
24
+ ## Compare manifests with backend initialization
27
25
 
28
- |Strategy|Scripts loaded|Data request starts|Banner visible|
29
- |--|--|--|--|
30
- |Client-only (no prefetch)|baseline|baseline|baseline|
31
- |Browser prefetch|\~1.3x faster|\~2.6x earlier|\~1.25x faster|
32
- |Server prefetch|\~2x faster|before page loads|\~1.9x faster|
26
+ A warm manifest cache can reduce initialization latency by avoiding an upstream
27
+ backend request. The saving depends on backend latency, cache hits and where
28
+ consent resolution sits in rendering. It is not a fixed speedup for the whole
29
+ page.
33
30
 
34
- ## 1) Prefer Same-Origin Rewrites
31
+ | Fetching path | Work during initialization |
32
+ | ----------------------- | ---------------------------------------------------------------------------------------------------- |
33
+ | Regular backend `/init` | Wait for the backend to resolve consent and return the result. |
34
+ | Cold manifest cache | Fetch public policy from the backend, cache it and resolve consent locally. |
35
+ | Warm manifest cache | Reuse cached policy and resolve consent locally; local route requests and rendering still take time. |
35
36
 
36
- Proxy c15t requests through your Next.js app so the browser calls your own origin instead of a third-party domain.
37
+ ### How much faster is a warm manifest?
37
38
 
38
- ```ts title="next.config.ts"
39
- import type { NextConfig } from 'next';
39
+ In a local SSR benchmark with **150 ms of simulated backend latency**, a warm
40
+ manifest reduced median time to first byte from **157.6 ms to 7.4 ms** across
41
+ 10 new-visitor samples per setup. Both setups waited for consent before rendering.
40
42
 
41
- const config: NextConfig = {
42
- async rewrites() {
43
- return [
44
- {
45
- source: '/api/c15t/:path*',
46
- destination: `${process.env.NEXT_PUBLIC_C15T_URL}/:path*`,
47
- },
48
- ];
49
- },
50
- };
43
+ The warm cache skipped the upstream policy fetch. A cold cache still needs that
44
+ fetch, so the benefit depends on your backend latency and cache hit rate.
51
45
 
52
- export default config;
53
- ```
46
+ See the [benchmark results](https://github.com/c15t/c15t/tree/ccfe906682ba06cef7b56a16cc8fd10728df77d3/benchmarks/reports/next-manifest-2026-09-10)
47
+ for the measurements and test conditions.
54
48
 
55
- Then use:
49
+ ## Keep browser consent requests on your origin
56
50
 
57
- ```tsx
58
- <ConsentManagerProvider options={{ backendURL: '/api/c15t', mode: 'hosted' }}>
59
- ```
51
+ A Next.js rewrite is an optional optimization. It can avoid a separate browser
52
+ DNS lookup and TLS connection to the consent backend when that connection would
53
+ otherwise be needed. Manifests and server rendering work without it.
60
54
 
61
- Why this helps:
55
+ The app server still connects to Inth, and the rewrite adds a proxy hop. Measure
56
+ your deployment to check the effect on latency. Vendor scripts, pixels and
57
+ iframes keep their own URLs; this rewrite only forwards c15t HTTP requests.
58
+ The same setup works with a self-hosted c15t backend.
62
59
 
63
- * Same-origin requests avoid extra DNS/TLS setup in many deployments
64
- * Ad blockers are less likely to block your init endpoint
65
- * You can change backend infrastructure without touching client code
60
+ ### Add the backend rewrite
66
61
 
67
- > ℹ️ **Info:**
68
- > Set NEXT\_PUBLIC\_C15T\_URL in .env to your backend URL, for example https\://your-project.inth.app.
69
- >
70
- > ⚠️ **Warning:**
71
- > Use rewrites for browser-side calls (ConsentManagerProvider, C15tPrefetch). For server-side fetchInitialData(), prefer a direct backend URL (for example https\://your-project.inth.app) to avoid an extra server proxy hop.
62
+ Set `C15T_BACKEND_URL` to the absolute endpoint supplied by your Inth project.
63
+ The endpoint is public configuration. Merge this fallback into your existing
64
+ Next.js configuration:
72
65
 
73
- ## 2) Choose A Startup Strategy
74
-
75
- Start with a same-origin rewrite and the default client-side provider. Add one of the preloading strategies below only when the route behavior or performance target calls for it.
76
-
77
- ### Client-Only Init
78
-
79
- Keep the default provider setup when you want the least complexity. This works on both static and dynamic routes, but the banner only appears after the client runtime starts and the initial `/init` request completes.
80
-
81
- ### Dynamic Routes: Fetch On The Server And Stream
82
-
83
- Use `fetchInitialData()` when the route is already dynamic, or when you are willing to make it dynamic in exchange for the fastest first banner.
66
+ ```ts title="next.config.ts"
67
+ import type { NextConfig } from 'next';
84
68
 
85
- Because it depends on `next/headers`, this opts the route into dynamic rendering.
69
+ const upstreamURL = process.env.C15T_BACKEND_URL?.replace(/\/$/, '');
70
+ if (!upstreamURL) throw new Error('Set C15T_BACKEND_URL to the absolute Inth endpoint');
86
71
 
87
- ```tsx title="app/layout.tsx"
88
- import { fetchInitialData } from '@c15t/nextjs';
89
- import ConsentManager from '@/components/consent-manager';
72
+ export default {
73
+ async rewrites() {
74
+ return {
75
+ fallback: [{
76
+ source: '/api/c15t/:path*',
77
+ destination: `${upstreamURL}/:path*`,
78
+ }],
79
+ };
80
+ },
81
+ } satisfies NextConfig;
82
+ ```
90
83
 
91
- export default function RootLayout({ children }: { children: React.ReactNode }) {
92
- const ssrData = fetchInitialData({
93
- backendURL: process.env.NEXT_PUBLIC_C15T_URL!,
94
- });
84
+ Next.js serves the local manifest handler first. Other consent requests, such
85
+ as `/api/c15t/subjects`, reach Inth through the rewrite. An optional local init
86
+ handler also takes precedence over this fallback.
95
87
 
96
- return (
97
- <html lang="en">
98
- <body>
99
- <ConsentManager ssrData={ssrData}>{children}</ConsentManager>
100
- </body>
101
- </html>
102
- );
103
- }
104
- ```
88
+ ### Use the local prefix in browser configuration
105
89
 
106
- ```tsx title="components/consent-manager/provider.tsx"
107
- 'use client';
108
-
109
- import { type ReactNode } from 'react';
110
- import {
111
- ConsentManagerProvider,
112
- ConsentBanner,
113
- ConsentDialog,
114
- type InitialDataPromise,
115
- } from '@c15t/nextjs';
116
-
117
- export default function ConsentManager({
118
- children,
119
- ssrData,
120
- }: {
121
- children: ReactNode;
122
- ssrData?: InitialDataPromise;
123
- }) {
124
- return (
125
- <ConsentManagerProvider
126
- options={{
127
- mode: 'hosted',
128
- backendURL: '/api/c15t',
129
- ssrData,
130
- }}
131
- >
132
- <ConsentBanner />
133
- <ConsentDialog />
134
- {children}
135
- </ConsentManagerProvider>
136
- );
137
- }
138
- ```
90
+ In the manifest setup from your router guide, change only `backendURL` to
91
+ `'/api/c15t'`. The shared configuration then looks like this:
139
92
 
140
- > ⚠️ **Warning:**
141
- > Do not await fetchInitialData(). Pass the unresolved Promise to the provider so Next.js can stream the route while /init runs in parallel.
142
- >
143
- > ℹ️ **Info:**
144
- > For fetchInitialData(), prefer a direct backend URL such as https\://your-project.inth.app instead of a rewrite to avoid an extra server-side proxy hop. See Server-Side Data Fetching for the full flow.
145
-
146
- ### Static Routes: Start Fetch Early In The Browser
147
-
148
- Use `C15tPrefetch` when the route needs to stay static but you still want the `/init` request to start before hydration. Matching prefetched data is consumed automatically by the runtime during first store initialization.
149
-
150
- ```tsx title="app/layout.tsx"
151
- import { C15tPrefetch } from '@c15t/nextjs';
152
- import { ConsentManager } from '@/components/consent-manager';
153
-
154
- export default function RootLayout({ children }: { children: React.ReactNode }) {
155
- return (
156
- <html lang="en">
157
- <head>
158
- <C15tPrefetch
159
- backendURL="/api/c15t"
160
- overrides={{ country: 'DE', region: 'BE', language: 'de' }}
161
- />
162
- </head>
163
- <body>
164
- <ConsentManager>{children}</ConsentManager>
165
- </body>
166
- </html>
167
- );
168
- }
169
- ```
93
+ ```ts title="c15t.config.ts"
94
+ import { defineConsentConfig } from 'c15t/next';
170
95
 
171
- ```tsx title="components/consent-manager/provider.tsx"
172
- 'use client';
173
-
174
- import {
175
- ConsentManagerProvider,
176
- ConsentBanner,
177
- ConsentDialog,
178
- } from '@c15t/nextjs';
179
-
180
- export default function ConsentManagerClient({ children }: { children: React.ReactNode }) {
181
- return (
182
- <ConsentManagerProvider
183
- options={{
184
- mode: 'hosted',
185
- backendURL: '/api/c15t',
186
- overrides: { country: 'DE', region: 'BE', language: 'de' },
187
- }}
188
- >
189
- <ConsentBanner />
190
- <ConsentDialog />
191
- {children}
192
- </ConsentManagerProvider>
193
- );
194
- }
96
+ export const consentConfig = defineConsentConfig({
97
+ backendURL: '/api/c15t',
98
+ manifestURL: '/api/c15t/manifest',
99
+ });
195
100
  ```
196
101
 
197
- > ℹ️ **Info:**
198
- > C15tPrefetch uses Next.js beforeInteractive script loading, so the /init request can start before hydration.
199
- >
200
- > ℹ️ **Info:**
201
- > If the request context changes between prefetch time and runtime, c15t falls back to a normal client /init. A common example is overrides.gpc conflicting with the browser's ambient GPC signal.
202
-
203
- ## Keep The Provider Mounted Across Navigation
102
+ The manifest URLs remain explicit in the current API. A configuration with only
103
+ `backendURL` uses regular backend `/init` and can also use this rewrite. That
104
+ setup must not have a local manifest init handler intercepting `/api/c15t/init`.
204
105
 
205
- Mount the consent provider at the app root so route transitions do not remount it.
106
+ Keep using `consentConfig` for `resolveConsent` and `ConsentRoot`. Omit a
107
+ separate `ConsentRoot` `backendURL` prop because it overrides the shared value.
206
108
 
207
- Why this helps:
109
+ ### Keep route handlers pointed upstream
208
110
 
209
- * Avoids re-running init work on client-side navigation
210
- * Prevents extra callback churn from remount cycles
211
- * Keeps banner/dialog state stable between route transitions
111
+ The ready-made App Router handlers read the absolute upstream URL from
112
+ `C15T_BACKEND_URL`. Keep these exports from the router guide:
212
113
 
213
- ## Animation Performance
114
+ ```ts title="app/api/c15t/manifest/route.ts"
115
+ export { manifestGET as GET } from 'c15t/next/api';
116
+ ```
214
117
 
215
- The default motion tokens are tuned for speed-first product UI:
118
+ ```ts title="app/api/c15t/init/route.ts"
119
+ export { GET } from 'c15t/next/api';
120
+ ```
216
121
 
217
- |Token|Duration|Used for|
218
- |--|--|--|
219
- |`fast`|80ms|Banner slide + overlay, card scale, button hover, widget entry/exit|
220
- |`normal`|150ms|Accordion, switch toggle|
221
- |`slow`|200ms|Dialog trigger snap, tab indicator|
122
+ Pages Router uses `createPagesApiHandlers()` with the same environment
123
+ configuration. Export `.manifest` and `.init` from the respective API files as
124
+ shown in the [Pages Router guide](./pages-router.md).
125
+
126
+ If you configure handlers with `createNextConsentRouteHandlers({ backendURL })`,
127
+ pass the absolute upstream URL. Passing the browser's local `consentConfig`
128
+ would make the manifest handler fetch itself. An explicit `C15T_MANIFEST_URL`
129
+ must also point upstream.
130
+
131
+ ### Check rewrite precedence
132
+
133
+ An array-form rewrite or `afterFiles` runs after concrete routes; `fallback`
134
+ runs after all application routes. A matching `beforeFiles` rewrite or
135
+ middleware/proxy rewrite can intercept the local handlers instead. See
136
+ [Next.js rewrite order](https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites).
137
+
138
+ Use `/api/c15t/:path*` for the backend prefix. A bare `/api/c15t` source does not
139
+ match nested endpoints. If a self-hosted backend catch-all already handles that
140
+ prefix, it serves the remaining endpoints instead of the fallback rewrite.
141
+ Keep the manifest resolver's upstream URL separate to avoid a request loop.
142
+
143
+ A full [static export](./static-export.md) has no Next.js
144
+ rewrites or API handlers. Use a hosting-level proxy or direct public Inth URL.
145
+
146
+ ## Configure manifest cache refresh
147
+
148
+ `createNextConsentRouteHandlers` uses the Next.js Data Cache plus an in-process
149
+ manifest cache. The in-process cache also covers Pages Router runtimes without
150
+ the Data Cache. It supports ETag revalidation and respects upstream cache
151
+ headers, including responses marked private.
152
+
153
+ Once the upstream `s-maxage` has passed, the in-process cache keeps serving the
154
+ cached manifest for as long as the upstream `stale-while-revalidate` allows
155
+ (an explicit `s-maxage` is required for that window to apply),
156
+ and refreshes it in the background. Requests do not wait for that refresh, and
157
+ a refresh that fails or times out leaves the cached manifest in place, so a
158
+ slow or unavailable backend does not delay rendering on a server that has
159
+ already loaded the manifest. Inth sends `s-maxage=300` and a 24 hour
160
+ `stale-while-revalidate` by default; lower the second value on a self-hosted
161
+ backend if a policy change must reach servers sooner after an outage. A server
162
+ with an empty cache still waits for the first upstream response.
163
+
164
+ The background refresh is detached from the request. On runtimes that stop
165
+ work once a response is sent, register it with the platform so it can finish.
166
+ Pass `onBackgroundRevalidate` to the handler factory; it is called inside the
167
+ handler with the refresh promise, which never rejects:
168
+
169
+ ```ts title="app/api/c15t/manifest/route.ts"
170
+ import { createNextConsentRouteHandlers } from 'c15t/next/api';
171
+ import { after } from 'next/server';
172
+
173
+ export const { manifestGET: GET } = createNextConsentRouteHandlers({
174
+ onBackgroundRevalidate: (refresh) => after(() => refresh),
175
+ });
176
+ ```
222
177
 
223
- These defaults follow the principle that product UI should be fast and purposeful — animations exist for spatial continuity, not decoration. In benchmarks, animation duration contributes a constant floor to "data fetched → banner visible" timing. The default tokens sit at the lower end of standard UI ranges (80-200ms) to minimize that floor.
178
+ The same option applies to the init handler's `GET`. `after` is stable from
179
+ Next 15.1; on Next 15.0 import `unstable_after` instead. Other hosts pass the
180
+ promise to their equivalent, such as `waitUntil` on Vercel or Cloudflare.
181
+ Without it the response still returns at once; only the refresh may be cut
182
+ short, in which case the next request starts another.
224
183
 
225
- To customize motion durations and easing, see [Styling](/docs/frameworks/next/styling/overview).
184
+ ## Avoid repeating startup work
226
185
 
227
- ## Reduce Network Overhead
186
+ Keep one boundary in the App Router root layout or Pages Router `_app.tsx`.
187
+ Remounting it during navigation creates another runtime and repeats startup
188
+ work. Keep the stylesheet and consent UI in that shared root too.
228
189
 
229
- If you must use a cross-origin backend URL, add preconnect so the browser starts DNS/TLS early:
190
+ Choose when to resolve consent according to your page. Use
191
+ [server rendering](./server-side.md) for resolved initial HTML
192
+ or streaming, and [client-side initialization](./client-side.md)
193
+ when the browser should prepare consent.
230
194
 
231
- ```tsx title="app/layout.tsx"
232
- <head>
233
- <link rel="preconnect" href="https://your-project.inth.app" crossOrigin="" />
234
- </head>
235
- ```
195
+ Measure cold entry, a warm manifest cache and client navigation separately.
196
+ Check server requests as well as the browser Network panel. Confirm that
197
+ consent saves reach the backend and policy updates appear after the configured
198
+ refresh window. If you add a rewrite, also confirm browser consent requests
199
+ use your origin.