@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
@@ -1,173 +1,140 @@
1
1
  ---
2
- title: Server-Side Data Fetching
3
- description: Pre-fetch consent data in Server Components with fetchInitialData
4
- for dynamic Next.js routes.
2
+ title: Server rendering and hydration
3
+ description: Resolve consent on the server, choose what waits for the result,
4
+ and preserve the same state through hydration.
5
5
  group: frameworks
6
6
  ---
7
- The `@c15t/nextjs` package provides `fetchInitialData`, a server-side function that fetches consent data before rendering. Use it when the route is already dynamic, or when you want the fastest first banner and are okay opting the route into dynamic rendering. If you need to keep a route fully static, use `C15tPrefetch` instead.
8
7
 
9
- Because `fetchInitialData()` resolves request headers from `next/headers`, using it opts the route into dynamic rendering.
8
+ ## Choose when consent resolves
10
9
 
11
- > ℹ️ **Info:**
12
- > SSR hydration is part of the initialization flow. When SSR data is available, the client skips the API fetch entirely.
10
+ Fetching and rendering are separate choices. The examples here keep the shared
11
+ `consentConfig` and manifest route from the
12
+ [App Router guide](./app-router.md).
13
13
 
14
- ```ts
15
- import { fetchInitialData } from '@c15t/nextjs';
16
- ```
14
+ | Rendering path | Pass to `ConsentRoot` as `state` | What the visitor receives |
15
+ | ------------------------------------------ | ---------------------------------------------- | ----------------------------------------------------------------------------------------------- |
16
+ | Await consent | The resolved `resolveConsent` result | The consent subtree renders after resolution. A surrounding Suspense fallback may stream first. |
17
+ | Stream while consent resolves | The `resolveConsent` promise | The page renders immediately; the browser initializes consent when the result arrives. |
18
+ | [Browser initialization](./client-side.md) | `state={{}}`, with the shared URLs in `config` | The browser initializes after mount; the initial HTML has no resolved consent prompt. |
17
19
 
18
- ## fetchInitialData
20
+ Client Components can still be prerendered by Next.js. Browser initialization
21
+ describes where consent resolves, not whether the component produces server HTML.
19
22
 
20
- The primary function for fetching consent data on the server. It calls the c15t backend's `/init` endpoint with the user's request headers (resolved automatically from `next/headers`) and returns the initial consent data.
23
+ ## Use the helper for your router
21
24
 
22
- ```ts title="app/layout.tsx"
23
- import { fetchInitialData } from '@c15t/nextjs';
25
+ | Router | Server helper import | Request context |
26
+ | ------------- | -------------------- | -------------------------------------- |
27
+ | App Router | `c15t/next/server` | Next.js request cookies and headers |
28
+ | Pages Router | `c15t/next/pages` | `req` passed from `getServerSideProps` |
29
+ | Static export | No request helper | Browser initialization |
24
30
 
25
- // In a Server Component — do NOT await
26
- const ssrData = fetchInitialData({
27
- backendURL: 'https://your-instance.c15t.dev',
28
- debug: process.env.NODE_ENV === 'development',
29
- });
30
- ```
31
+ For complete setup files, follow [App Router](./app-router.md),
32
+ [Pages Router](./pages-router.md) or
33
+ [static export](./static-export.md).
34
+ Pages Router must await prefetch inside `getServerSideProps` before serializing
35
+ props. The streaming examples on this page are for App Router.
31
36
 
32
- > ⚠️ **Warning:**
33
- > Do not await fetchInitialData() in Server Components. Pass the Promise directly to your client component so Next.js can stream the page while the consent data loads in parallel.
34
- >
35
- > ℹ️ **Info:**
36
- > For fetchInitialData(), prefer a direct backend URL such as https\://your-instance.c15t.dev. For browser-side calls from the provider, C15tPrefetch, and other client runtime work, prefer a same-origin /api/c15t rewrite. See Optimization for the full decision guide.
37
- >
38
- > ℹ️ **Info:**
39
- > Need fully static routes? Use C15tPrefetch in your layout. Matching prefetched data is consumed automatically by the runtime instead of using fetchInitialData(). See Optimization.
40
-
41
- ### How Streaming Works
42
-
43
- The diagram below shows how the prefetch avoids blocking the page render. The server fires the `/init` request and immediately starts streaming HTML — the resolved consent data is sent as a later chunk once the backend responds.
44
-
45
- ```mermaid
46
- sequenceDiagram
47
- participant SC as Server Component
48
- participant NR as Next.js Runtime
49
- participant BR as Browser
50
- participant BE as c15t Backend
51
-
52
- SC->>BE: fetchInitialData() → GET /init
53
- Note right of SC: Returns a Promise<br/>(not awaited)
54
- SC->>NR: Render page tree<br/>(Promise passed as prop)
55
- NR->>BR: Stream initial HTML
56
- BR->>BR: Hydrate — no consent data yet
57
-
58
- BE-->>NR: /init response resolves
59
- NR->>BR: Stream resolved data chunk
60
- BR->>BR: Provider receives SSR data,<br/>skips client-side /init fetch
61
- ```
37
+ ## Stream the page while consent resolves
62
38
 
63
- ### Options
39
+ The App Router guide awaits `resolveConsent` inside `Suspense` by
40
+ default and offers this promise form as the streaming alternative. Pass the
41
+ promise from a synchronous layout to the script-owning client wrapper. This
42
+ partial example keeps that wrapper:
64
43
 
65
- |Property|Value|
66
- |:--|:--|
67
- |Type Name|\`FetchInitialDataOptions\`|
68
- |Source Path|\`./packages/nextjs/src/types.ts\`|
44
+ ```tsx
45
+ import { resolveConsent } from 'c15t/next/server';
46
+ import { consentConfig } from '../c15t.config';
47
+ import { Consent } from '../components/consent';
69
48
 
70
- \*ExtractedTypeTable: Could not extract "FetchInitialDataOptions" from "./packages/nextjs/src/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
49
+ // Inside the existing synchronous layout:
50
+ const state = resolveConsent({ config: consentConfig });
71
51
 
72
- ### Return Value
52
+ <Consent state={state}>{children}</Consent>
53
+ ```
73
54
 
74
- Returns `Promise<SSRInitialData | undefined>`. The data includes the init response (jurisdiction, translations, consent model) and GVL data when IAB is configured.
55
+ This promise form does not render consent UI on the server. For server-rendered
56
+ consent UI, await the result as shown below.
75
57
 
76
- ### Passing SSR Data to the Provider
58
+ Keep the shared config and manifest route from that guide. This form also
59
+ works with `cacheComponents: true`. Request helpers still need a visitor request;
60
+ passing a promise does not make this a static-export setup.
77
61
 
78
- Pass the unresolved Promise to the provider's `ssrData` option via a client component:
62
+ ## Await consent inside Suspense
79
63
 
80
- ```tsx title="components/consent-manager/index.tsx"
81
- 'use client';
64
+ If the consent subtree should wait for resolution, move it into an async Server
65
+ Component under `Suspense`. With `cacheComponents: true`, awaiting request data
66
+ directly in the root layout without this boundary causes a build error.
82
67
 
83
- import { type ReactNode } from 'react';
84
- import { ConsentManagerProvider, ConsentBanner, ConsentDialog } from '@c15t/nextjs';
85
- import type { InitialDataPromise } from '@c15t/nextjs';
68
+ This is the default root layout from the App Router guide. Keep its shared
69
+ `c15t.config.ts`, manifest route and script-owning `Consent` wrapper:
70
+
71
+ ```tsx title="app/layout.tsx"
72
+ import { Suspense } from 'react';
73
+ import type { ReactNode } from 'react';
74
+ import { Consent } from '../components/consent';
75
+ import { resolveConsent } from 'c15t/next/server';
76
+ import { consentConfig } from '../c15t.config';
77
+ import './globals.css';
78
+
79
+ async function ResolvedConsent({ children }: { children: ReactNode }) {
80
+ const state = await resolveConsent({ config: consentConfig });
86
81
 
87
- export default function ConsentManager({
88
- children,
89
- ssrData,
90
- }: {
91
- children: ReactNode;
92
- ssrData?: InitialDataPromise;
93
- }) {
94
82
  return (
95
- <ConsentManagerProvider
96
- options={{
97
- mode: 'hosted',
98
- backendURL: '/api/c15t',
99
- ssrData,
100
- }}
101
- >
102
- <ConsentBanner />
103
- <ConsentDialog />
104
- {children}
105
- </ConsentManagerProvider>
83
+ <Consent state={state}>{children}</Consent>
106
84
  );
107
85
  }
108
- ```
109
-
110
- ```tsx title="app/layout.tsx"
111
- import { fetchInitialData } from '@c15t/nextjs';
112
- import ConsentManager from '@/components/consent-manager';
113
-
114
- export default function RootLayout({ children }: { children: React.ReactNode }) {
115
- const ssrData = fetchInitialData({
116
- backendURL: 'https://your-instance.c15t.dev',
117
- });
118
86
 
87
+ export default function RootLayout({ children }: { children: ReactNode }) {
119
88
  return (
120
89
  <html lang="en">
121
90
  <body>
122
- <ConsentManager ssrData={ssrData}>
123
- {children}
124
- </ConsentManager>
91
+ <Suspense fallback={null}>
92
+ <ResolvedConsent>{children}</ResolvedConsent>
93
+ </Suspense>
125
94
  </body>
126
95
  </html>
127
96
  );
128
97
  }
129
98
  ```
130
99
 
131
- ## How Headers Are Resolved
100
+ This example waits to render the children inside `ResolvedConsent`. The outer
101
+ shell and fallback can stream first, so awaiting does not guarantee a resolved
102
+ prompt in the first HTML response. Use the promise form when the page should
103
+ render while consent is pending.
132
104
 
133
- Unlike `@c15t/react/server` where you must pass headers manually, `fetchInitialData` uses `next/headers` to automatically resolve the incoming request headers. This means geo-location headers from Vercel, Cloudflare, or AWS CloudFront are forwarded to the c15t backend without any extra configuration.
105
+ ## Preserve the server result
134
106
 
135
- |Header|Source|Contains|
136
- |--|--|--|
137
- |`cf-ipcountry`|Cloudflare|Country code|
138
- |`x-vercel-ip-country`|Vercel|Country code|
139
- |`x-amz-cf-ipcountry`|AWS CloudFront|Country code|
140
- |`x-vercel-ip-country-region`|Vercel|Region code|
141
- |`accept-language`|Browser|Language preference|
142
- |`x-forwarded-host`|Proxy|Original host|
143
- |`x-forwarded-for`|Proxy|Client IP|
107
+ `resolveConsent` restores records and resolves policy for the request.
108
+ With `manifestURL`, it resolves the configured manifest. With only `backendURL`,
109
+ it calls `${backendURL}/init`. Changing when you await the helper does not change
110
+ which fetching path it uses.
144
111
 
145
- ## Advanced: Using @c15t/react/server Directly
112
+ Pass the result intact to `state` and keep the same shared URLs in `config`.
113
+ Do not turn allowed categories into new saved choices or emit a choice event
114
+ during hydration. Keep one `ConsentRoot` mounted across navigation.
146
115
 
147
- For advanced use cases (custom server frameworks, edge functions, or non-standard header resolution), the underlying utilities from `@c15t/react/server` are available:
116
+ With resolved server state, `Frame` renders its placeholder or permitted content
117
+ in the server HTML and reuses that state during hydration. It does not need to
118
+ wait for the browser to mount before showing the placeholder. The default
119
+ placeholder is visible immediately, without a fade-in animation.
148
120
 
149
- ```ts
150
- import {
151
- fetchSSRData,
152
- extractRelevantHeaders,
153
- normalizeBackendURL,
154
- validateBackendURL,
155
- } from '@c15t/react/server';
156
- ```
121
+ A resolved policy can satisfy browser initialization without another request.
122
+ If prefetch fails or returns only baseline records, the browser can retry.
123
+ Until resolution succeeds, optional permissions remain denied and consent UI
124
+ stays hidden. See [Troubleshooting](./troubleshooting.md).
157
125
 
158
- See the [React Server-Side Utilities](/docs/frameworks/react/server-side) docs for full API details.
126
+ `resolveConsent()` called without `config` or `backendURL` only reads request
127
+ cookies, location, language and privacy signals. It makes no backend request
128
+ and does not resolve policy. Pass `config` when rendering needs the resolved
129
+ policy.
159
130
 
160
- ## Debugging SSR
131
+ ## Cache policy data without sharing visitor state
161
132
 
162
- Use the [useSSRStatus](/docs/frameworks/next/hooks/use-ssr-status) hook on the client to verify SSR data was consumed:
133
+ A manifest is public policy configuration. Resolved init data, stored records
134
+ and personalized HTML belong to a visitor. Keep them out of shared caches unless
135
+ you provide appropriate isolation. Test more than one visitor and location.
163
136
 
164
- ```tsx
165
- import { useSSRStatus } from '@c15t/nextjs';
166
-
167
- function DebugSSR() {
168
- const { ssrDataUsed, ssrSkippedReason } = useSSRStatus();
169
-
170
- if (ssrDataUsed) return <span>SSR hydration successful</span>;
171
- return <span>SSR skipped: {ssrSkippedReason ?? 'unknown'}</span>;
172
- }
173
- ```
137
+ See [Optimization](./optimization.md) for cache settings and the
138
+ [fetching reference](./api-reference/data-fetching.md#geography-and-privacy-signals)
139
+ for trusted location headers. Same-origin rewrites are optional and do not enable
140
+ server rendering.
@@ -0,0 +1,164 @@
1
+ ---
2
+ title: Static export
3
+ description: Add c15t to output export without request helpers or a local API server.
4
+ group: frameworks
5
+ ---
6
+
7
+ [Create an Inth project](https://inth.com) and use its backend endpoint in
8
+ this guide. Configure the project's policy rules and allowed app origins first.
9
+
10
+ ## Use a browser ConsentRoot
11
+
12
+ A Next.js static export has no runtime server for `/api/c15t`, Next.js rewrites or
13
+ request cookies. Point the browser at an absolute consent backend URL. Static
14
+ hosting does not require browser-only consent storage.
15
+
16
+ | Package manager | Command |
17
+ | :-------------- | :----------------- |
18
+ | npm | `npm install c15t` |
19
+ | pnpm | `pnpm add c15t` |
20
+ | yarn | `yarn add c15t` |
21
+ | bun | `bun add c15t` |
22
+
23
+ ```ts title="next.config.ts"
24
+ import type { NextConfig } from 'next';
25
+
26
+ const config: NextConfig = { output: 'export' };
27
+ export default config;
28
+ ```
29
+
30
+ ```tsx title="components/consent.tsx"
31
+ 'use client';
32
+
33
+ import type { ReactNode } from 'react';
34
+ import {
35
+ ConsentBanner,
36
+ ConsentDialog,
37
+ ConsentDialogLink,
38
+ ConsentRoot,
39
+ } from 'c15t/next';
40
+
41
+ export function Consent({ children }: { children: ReactNode }) {
42
+ const backendURL = process.env.NEXT_PUBLIC_C15T_BACKEND_URL;
43
+ if (!backendURL) throw new Error('Set NEXT_PUBLIC_C15T_BACKEND_URL');
44
+ return (
45
+ <ConsentRoot state={{}} backendURL={backendURL}>
46
+ {children}
47
+ <ConsentBanner />
48
+ <ConsentDialog />
49
+ <ConsentDialogLink>Privacy settings</ConsentDialogLink>
50
+ </ConsentRoot>
51
+ );
52
+ }
53
+ ```
54
+
55
+ Add c15t to the global stylesheet shared by your app. With Tailwind CSS 4,
56
+ keep its import first:
57
+
58
+ ```css title="styles/globals.css"
59
+ @import 'tailwindcss';
60
+ @import 'c15t/next/styles.css';
61
+ ```
62
+
63
+ Without Tailwind, omit its import. For Tailwind CSS 3, use the
64
+ [separate stylesheet recipe](./styling/overview.md#stylesheet).
65
+ Keep any existing app styles and adjust the imports below to their location.
66
+
67
+ For App Router, mount the wrapper in the root layout:
68
+
69
+ ```tsx title="app/layout.tsx"
70
+ import type { ReactNode } from 'react';
71
+ import { Consent } from '../components/consent';
72
+ import '../styles/globals.css';
73
+
74
+ export default function RootLayout({ children }: { children: ReactNode }) {
75
+ return <html lang="en"><body><Consent>{children}</Consent></body></html>;
76
+ }
77
+ ```
78
+
79
+ For Pages Router, mount the same wrapper in `_app.tsx`:
80
+
81
+ ```tsx title="pages/_app.tsx"
82
+ import type { AppProps } from 'next/app';
83
+ import { Consent } from '../components/consent';
84
+ import '../styles/globals.css';
85
+
86
+ export default function App({ Component, pageProps }: AppProps) {
87
+ return <Consent><Component {...pageProps} /></Consent>;
88
+ }
89
+ ```
90
+
91
+ The imports assume `app/`, `pages/` and `components/` are at the project root.
92
+ Adjust them if your project uses `src/`.
93
+
94
+ Set `NEXT_PUBLIC_C15T_BACKEND_URL` when building the site. Next.js embeds public
95
+ environment values into the browser bundle, so changing this URL requires a new
96
+ build. Configure the exported site's origin in the backend's trusted origins.
97
+
98
+ ## Test the exported files
99
+
100
+ Run your production build and serve the generated `out/` directory with a
101
+ static server. A Next.js development server can hide accidental dependencies on
102
+ API routes. Inspect Network for failed same-origin `/api/c15t` calls, then test
103
+ reject, reload, preference changes and an unavailable backend using
104
+ [verification](../../guides/verify-consent.md).
105
+
106
+ ## What happens before initialization?
107
+
108
+ The exported HTML cannot contain a choice resolved for an individual visitor.
109
+ The browser restores valid records and resolves the policy. Until a policy
110
+ resolves, optional permissions remain denied and the consent UI stays hidden.
111
+ Do not insert an unconditional analytics script into the exported HTML to work
112
+ around that pending state.
113
+
114
+ ## Use an external manifest when you need client resolution
115
+
116
+ Regular Inth `/init` is the simpler hosted path for static export. If you want
117
+ the browser to resolve a manifest instead, change the existing `ConsentRoot`
118
+ to use a consent config. Set `NEXT_PUBLIC_C15T_MANIFEST_URL` to a reachable
119
+ absolute manifest endpoint and keep the same backend URL for saves.
120
+
121
+ This is a partial change to `components/consent.tsx`:
122
+
123
+ ```tsx
124
+ import { defineConsentConfig } from 'c15t/next';
125
+
126
+ const backendURL = process.env.NEXT_PUBLIC_C15T_BACKEND_URL;
127
+ const manifestURL = process.env.NEXT_PUBLIC_C15T_MANIFEST_URL;
128
+ if (!backendURL || !manifestURL) throw new Error('Set the public consent URLs');
129
+
130
+ const consentConfig = defineConsentConfig({ backendURL, manifestURL });
131
+
132
+ // Keep the existing children, banner, dialog and preferences link.
133
+ <ConsentRoot state={{}} config={consentConfig}>
134
+ {children}
135
+ </ConsentRoot>
136
+ ```
137
+
138
+ Do not set `initURL` to a local Next.js API route in an export. The browser
139
+ loads the manifest resolver lazily and resolves unknown geography unless your
140
+ application supplies location through a separate design. It cannot read a
141
+ server's geographic request headers. Ensure the manifest and record endpoints
142
+ allow requests from the exported site's origin.
143
+
144
+ For build-time policy snapshots, `c15t/next/static` exports
145
+ `loadStaticManifest`, `createStaticManifestModule` and
146
+ `createStaticConsentResolver`. A generated manifest contains public policy,
147
+ not a visitor's resolved choice. `createStaticConsentResolver` returns a
148
+ synchronous `initial` result and a `resolved` promise that can use `geo` or a
149
+ `geoURL`. These helpers do not mount a provider, persist choices or configure
150
+ consent writes for you. Plan that wiring and policy refresh before choosing a
151
+ build-time snapshot; publishing new policy may require a rebuild.
152
+
153
+ Never bake a build machine's location or cookies into a shared static page.
154
+ Unknown geography does not make every regional rule applicable.
155
+
156
+ ## Use offline mode for local-only records
157
+
158
+ Not recommended for production environments. Use it for local development and
159
+ tests; a static production site can still connect to Inth.
160
+
161
+ If the site deliberately needs no consent backend, use the explicit
162
+ [offline boundary example](./api-reference/data-fetching.md#offline-configuration).
163
+ It resolves bundled rules and stores choices in the browser. Static hosting
164
+ alone is not a reason to give up Inth policy management and consent records.