@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.
- package/AGENTS.md +102 -142
- package/README.md +4 -4
- package/dist/api.js +1 -0
- package/dist/config.js +1 -0
- package/dist/devtools.js +2 -0
- package/dist/headers.js +1 -0
- package/dist/iab/styles.css +1 -1
- package/dist/iab/styles.tw3.css +133 -19
- package/dist/index.js +1 -1
- package/dist/middleware.js +1 -0
- package/dist/node-bridge.js +1 -0
- package/dist/pages.js +1 -0
- package/dist/proxy.js +1 -0
- package/dist/root.js +2 -0
- package/dist/server.js +1 -0
- package/dist/static.js +1 -0
- package/dist/styles.css +1 -1
- package/dist/styles.tw3.css +173 -29
- package/dist/version.js +1 -1
- package/dist-types/api.d.ts +88 -0
- package/dist-types/config.d.ts +110 -0
- package/dist-types/devtools.d.ts +1 -0
- package/dist-types/headers.d.ts +4 -0
- package/dist-types/index.d.ts +28 -10
- package/dist-types/middleware.d.ts +15 -0
- package/dist-types/node-bridge.d.ts +57 -0
- package/dist-types/pages.d.ts +88 -0
- package/dist-types/proxy.d.ts +34 -0
- package/dist-types/root.d.ts +86 -0
- package/dist-types/server.d.ts +164 -0
- package/dist-types/static.d.ts +42 -0
- package/dist-types/types.d.ts +5 -36
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +102 -142
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/customization/overview.md +45 -0
- package/docs/customization/recipes.md +79 -0
- package/docs/customization/slots.md +55 -0
- package/docs/customization/tokens.md +76 -0
- package/docs/customization/translations.md +49 -0
- package/docs/frameworks/next/api-reference/data-fetching.md +416 -0
- package/docs/frameworks/next/app-router.md +403 -0
- package/docs/frameworks/next/client-side.md +118 -0
- package/docs/frameworks/next/components/consent-banner.md +251 -211
- package/docs/frameworks/next/components/consent-dialog-link.md +96 -35
- package/docs/frameworks/next/components/consent-dialog-trigger.md +74 -149
- package/docs/frameworks/next/components/consent-dialog.md +189 -134
- package/docs/frameworks/next/components/consent-manager-provider.md +60 -318
- package/docs/frameworks/next/components/consent-widget.md +172 -114
- package/docs/frameworks/next/components/dev-tools.md +199 -40
- package/docs/frameworks/next/components/frame.md +137 -42
- package/docs/frameworks/next/concepts/consent-categories.md +24 -89
- package/docs/frameworks/next/concepts/policy-presets.md +142 -0
- package/docs/frameworks/next/content-security-policy.md +189 -0
- package/docs/frameworks/next/data-fetching.md +74 -0
- package/docs/frameworks/next/geography-headers.md +251 -0
- package/docs/frameworks/next/headless.md +95 -185
- package/docs/frameworks/next/hooks/use-consent-manager/overview.md +42 -163
- package/docs/frameworks/next/iab/overview.md +37 -107
- package/docs/frameworks/next/optimization.md +158 -194
- package/docs/frameworks/next/pages-router.md +296 -0
- package/docs/frameworks/next/quickstart.md +31 -132
- package/docs/frameworks/next/script-loader.md +134 -467
- package/docs/frameworks/next/server-side.md +97 -130
- package/docs/frameworks/next/static-export.md +164 -0
- package/docs/frameworks/next/styling/overview.md +166 -250
- package/docs/frameworks/next/troubleshooting.md +134 -144
- package/docs/guides/consent-state.md +60 -0
- package/docs/guides/data-fetching.md +163 -0
- package/docs/guides/deployment-modes.md +63 -0
- package/docs/guides/troubleshooting.md +68 -0
- package/docs/guides/verify-consent.md +62 -0
- package/docs/integrations/adobe-analytics.md +239 -105
- package/docs/integrations/ahrefs-analytics.md +238 -104
- package/docs/integrations/amplitude.md +219 -157
- package/docs/integrations/building-integrations.md +32 -224
- package/docs/integrations/clearbit.md +247 -86
- package/docs/integrations/cloudflare-web-analytics.md +250 -84
- package/docs/integrations/crisp.md +251 -97
- package/docs/integrations/databuddy.md +259 -153
- package/docs/integrations/fathom-analytics.md +239 -96
- package/docs/integrations/google-maps.md +328 -207
- package/docs/integrations/google-tag-manager.md +248 -96
- package/docs/integrations/google-tag.md +261 -90
- package/docs/integrations/heap.md +222 -149
- package/docs/integrations/hightouch.md +225 -131
- package/docs/integrations/hotjar.md +239 -90
- package/docs/integrations/intercom.md +239 -98
- package/docs/integrations/linkedin-insights.md +243 -113
- package/docs/integrations/logrocket.md +241 -123
- package/docs/integrations/matomo-analytics.md +256 -111
- package/docs/integrations/meta-pixel.md +197 -324
- package/docs/integrations/microsoft-clarity.md +233 -114
- package/docs/integrations/microsoft-uet.md +245 -110
- package/docs/integrations/mixpanel-analytics.md +252 -87
- package/docs/integrations/openai-pixel.md +441 -0
- package/docs/integrations/overview.md +95 -133
- package/docs/integrations/pirsch.md +249 -96
- package/docs/integrations/plausible-analytics.md +241 -100
- package/docs/integrations/posthog.md +353 -214
- package/docs/integrations/promptwatch.md +251 -81
- package/docs/integrations/reddit-pixel.md +226 -173
- package/docs/integrations/rudderstack.md +244 -187
- package/docs/integrations/rybbit-analytics.md +244 -91
- package/docs/integrations/segment.md +238 -92
- package/docs/integrations/snapchat-pixel.md +240 -110
- package/docs/integrations/tiktok-pixel.md +249 -81
- package/docs/integrations/umami-analytics.md +242 -95
- package/docs/integrations/vercel-analytics.md +242 -90
- package/docs/integrations/x-pixel.md +238 -104
- package/docs/integrations/youtube.md +354 -142
- package/docs/upgrade-v3.md +334 -0
- package/iab/styles.css +1 -1
- package/iab/styles.tw3.css +1 -1
- package/package.json +106 -65
- package/readme.json +3 -3
- package/src/iab/styles.css +1 -1
- package/src/iab/styles.tw3.css +1 -1
- package/src/styles.css +1 -1
- package/src/styles.tw3.css +1 -1
- package/styles.css +1 -1
- package/styles.tw3.css +1 -1
- package/client/components/consent-dialog-link.js +0 -3
- package/client/components/integrations.js +0 -3
- package/dist/components/integrations/index.cjs +0 -1
- package/dist/components/integrations/index.js +0 -1
- package/dist/headless.cjs +0 -1
- package/dist/index.cjs +0 -1
- package/dist/libs/browser-initial-data.cjs +0 -1
- package/dist/libs/browser-initial-data.js +0 -1
- package/dist/libs/initial-data.cjs +0 -1
- package/dist/libs/initial-data.js +0 -1
- package/dist/types.cjs +0 -1
- package/dist/version.cjs +0 -1
- package/dist-types/components/integrations/index.d.ts +0 -1
- package/dist-types/libs/browser-initial-data.d.ts +0 -9
- package/dist-types/libs/initial-data.d.ts +0 -33
- package/docs/frameworks/next/building-headless-components.md +0 -379
- package/docs/frameworks/next/callbacks.md +0 -186
- package/docs/frameworks/next/concepts/client-modes.md +0 -177
- package/docs/frameworks/next/concepts/consent-models.md +0 -117
- package/docs/frameworks/next/concepts/cookie-management.md +0 -122
- package/docs/frameworks/next/concepts/glossary.md +0 -24
- package/docs/frameworks/next/concepts/initialization-flow.md +0 -149
- package/docs/frameworks/next/concepts/policy-packs.md +0 -230
- package/docs/frameworks/next/hooks/use-color-scheme.md +0 -41
- package/docs/frameworks/next/hooks/use-consent-manager/checking-consent.md +0 -96
- package/docs/frameworks/next/hooks/use-consent-manager/location-info.md +0 -97
- package/docs/frameworks/next/hooks/use-consent-manager/setting-consent.md +0 -94
- package/docs/frameworks/next/hooks/use-draggable.md +0 -59
- package/docs/frameworks/next/hooks/use-focus-trap.md +0 -42
- package/docs/frameworks/next/hooks/use-reduced-motion.md +0 -37
- package/docs/frameworks/next/hooks/use-ssr-status.md +0 -32
- package/docs/frameworks/next/hooks/use-text-direction.md +0 -50
- package/docs/frameworks/next/hooks/use-translations.md +0 -55
- package/docs/frameworks/next/iab/consent-banner.md +0 -91
- package/docs/frameworks/next/iab/consent-dialog.md +0 -129
- package/docs/frameworks/next/iab/use-gvl-data.md +0 -21
- package/docs/frameworks/next/iframe-blocking.md +0 -106
- package/docs/frameworks/next/internationalization.md +0 -207
- package/docs/frameworks/next/network-blocker.md +0 -140
- package/docs/frameworks/next/policy-packs.md +0 -248
- package/docs/frameworks/next/styling/classnames.md +0 -94
- package/docs/frameworks/next/styling/color-scheme.md +0 -84
- package/docs/frameworks/next/styling/css-variables.md +0 -53
- package/docs/frameworks/next/styling/slots.md +0 -94
- package/docs/frameworks/next/styling/tailwind.md +0 -137
- package/docs/frameworks/next/styling/tokens.md +0 -156
- package/docs/shared/concepts/client-modes.md +0 -103
- package/docs/shared/concepts/consent-categories.md +0 -41
- package/docs/shared/concepts/consent-models.md +0 -72
- package/docs/shared/concepts/cookie-management.md +0 -88
- package/docs/shared/concepts/glossary.md +0 -24
- package/docs/shared/concepts/initialization-flow.md +0 -105
- package/docs/shared/concepts/policy-packs.md +0 -225
- package/docs/shared/react/components/consent-banner.md +0 -242
- package/docs/shared/react/components/consent-dialog-link.md +0 -45
- package/docs/shared/react/components/consent-dialog-trigger.md +0 -185
- package/docs/shared/react/components/consent-dialog.md +0 -119
- package/docs/shared/react/components/consent-manager-provider.md +0 -225
- package/docs/shared/react/components/consent-widget.md +0 -121
- package/docs/shared/react/components/dev-tools.md +0 -81
- package/docs/shared/react/components/frame.md +0 -52
- package/docs/shared/react/guides/building-headless-components.md +0 -110
- package/docs/shared/react/guides/callbacks.md +0 -89
- package/docs/shared/react/guides/headless.md +0 -31
- package/docs/shared/react/guides/iframe-blocking.md +0 -65
- package/docs/shared/react/guides/internationalization.md +0 -123
- package/docs/shared/react/guides/network-blocker.md +0 -72
- package/docs/shared/react/guides/optimization.md +0 -44
- package/docs/shared/react/guides/policy-packs.md +0 -173
- package/docs/shared/react/guides/script-loader.md +0 -311
- package/docs/shared/react/hooks/use-color-scheme.md +0 -31
- package/docs/shared/react/hooks/use-consent-manager/checking-consent.md +0 -95
- package/docs/shared/react/hooks/use-consent-manager/location-info.md +0 -96
- package/docs/shared/react/hooks/use-consent-manager/overview.md +0 -74
- package/docs/shared/react/hooks/use-consent-manager/setting-consent.md +0 -93
- package/docs/shared/react/hooks/use-draggable.md +0 -30
- package/docs/shared/react/hooks/use-focus-trap.md +0 -20
- package/docs/shared/react/hooks/use-reduced-motion.md +0 -33
- package/docs/shared/react/hooks/use-ssr-status.md +0 -16
- package/docs/shared/react/hooks/use-text-direction.md +0 -38
- package/docs/shared/react/hooks/use-translations.md +0 -15
- package/docs/shared/react/iab/consent-banner.md +0 -60
- package/docs/shared/react/iab/consent-dialog.md +0 -76
- package/docs/shared/react/iab/overview.md +0 -80
- package/docs/shared/react/iab/use-gvl-data.md +0 -21
- package/docs/shared/react/styling/classnames.md +0 -93
- package/docs/shared/react/styling/color-scheme.md +0 -35
- package/docs/shared/react/styling/css-variables.md +0 -53
- package/docs/shared/react/styling/overview.md +0 -261
- package/docs/shared/react/styling/slots.md +0 -93
- package/docs/shared/react/styling/stylesheet-entrypoint.md +0 -8
- package/docs/shared/react/styling/tailwind.md +0 -88
- package/docs/shared/react/styling/tokens.md +0 -155
- package/docs/shared/troubleshooting.md +0 -82
|
@@ -1,173 +1,140 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Server
|
|
3
|
-
description:
|
|
4
|
-
|
|
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
|
-
|
|
8
|
+
## Choose when consent resolves
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
## Use the helper for your router
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
44
|
+
```tsx
|
|
45
|
+
import { resolveConsent } from 'c15t/next/server';
|
|
46
|
+
import { consentConfig } from '../c15t.config';
|
|
47
|
+
import { Consent } from '../components/consent';
|
|
69
48
|
|
|
70
|
-
|
|
49
|
+
// Inside the existing synchronous layout:
|
|
50
|
+
const state = resolveConsent({ config: consentConfig });
|
|
71
51
|
|
|
72
|
-
|
|
52
|
+
<Consent state={state}>{children}</Consent>
|
|
53
|
+
```
|
|
73
54
|
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
+
## Await consent inside Suspense
|
|
79
63
|
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
123
|
-
{children}
|
|
124
|
-
</
|
|
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
|
-
|
|
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
|
-
|
|
105
|
+
## Preserve the server result
|
|
134
106
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
131
|
+
## Cache policy data without sharing visitor state
|
|
161
132
|
|
|
162
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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.
|