@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
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Fetching reference
|
|
3
|
+
description: Reference for Next.js consent URLs, manifest resolution, request
|
|
4
|
+
geography and offline configuration.
|
|
5
|
+
group: frameworks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## URL configuration
|
|
9
|
+
|
|
10
|
+
`defineConsentConfig` from `c15t/next` declares the URLs shared by
|
|
11
|
+
`resolveConsent` and `ConsentRoot`. For help choosing a setup, start with
|
|
12
|
+
[data fetching](../data-fetching.md).
|
|
13
|
+
Requires Next.js 15 or 16 (`next ^15.0.0 || ^16.0.0`).
|
|
14
|
+
|
|
15
|
+
| Property | Behavior |
|
|
16
|
+
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `backendURL` | Required backend base URL. Consent submissions go to `/subjects` under this URL. Without `manifestURL`, server prefetch calls its `/init` endpoint. |
|
|
18
|
+
| `manifestURL` | Optional URL that serves the policy manifest. Setting it enables manifest resolution. |
|
|
19
|
+
| `initURL` | Optional browser initialization endpoint. With the local manifest handler, this resolves policy using the browser request's geography. |
|
|
20
|
+
|
|
21
|
+
The current API does not derive `manifestURL` or `initURL` from `backendURL`,
|
|
22
|
+
and declaring URLs does not create routes. Values can be absolute HTTP or
|
|
23
|
+
HTTPS URLs or paths beginning with `/`. Server helpers resolve relative paths
|
|
24
|
+
against the incoming request's host.
|
|
25
|
+
|
|
26
|
+
`defineConsentConfig` validates at call time and throws a `TypeError` when the
|
|
27
|
+
argument is not an object, when `backendURL` is missing, or when any URL is
|
|
28
|
+
neither an absolute `http(s)` URL nor a `/`-relative path. Protocol-relative
|
|
29
|
+
`//consent.example.com` and bare `api/c15t` are rejected because the server
|
|
30
|
+
helpers would resolve them against the request host and reach an unintended
|
|
31
|
+
origin. The returned object is frozen. When `initURL` is set without
|
|
32
|
+
`manifestURL` and `NODE_ENV` is not `production`, it logs a `console.warn`:
|
|
33
|
+
browser initialization would use the init route, but server prefetch would
|
|
34
|
+
still call backend `/init` on every request. Set `manifestURL` so both sides
|
|
35
|
+
resolve the cached manifest.
|
|
36
|
+
|
|
37
|
+
### Why are the URLs separate?
|
|
38
|
+
|
|
39
|
+
`backendURL` usually points to Inth, while the manifest and local init routes
|
|
40
|
+
belong to your Next.js application. Appending `/manifest` to the Inth URL would
|
|
41
|
+
select Inth's endpoint, not create or select your app's cached manifest route.
|
|
42
|
+
Using `/api/c15t` as the backend prefix through an optional rewrite does not
|
|
43
|
+
change the config defaults.
|
|
44
|
+
|
|
45
|
+
The manifest **handler** does derive its upstream `/manifest` URL from its
|
|
46
|
+
backend URL. That server-side default is separate from `defineConsentConfig`,
|
|
47
|
+
which keeps the URLs you pass to it and does not choose a fetching mode for you.
|
|
48
|
+
|
|
49
|
+
### Do I need the local init route?
|
|
50
|
+
|
|
51
|
+
No. The router guides use server prefetch and one manifest route. Successful
|
|
52
|
+
prefetch supplies the initial policy directly to the browser.
|
|
53
|
+
|
|
54
|
+
Add a local init route when browser initialization needs your host's location
|
|
55
|
+
headers or you want to keep manifest resolution on the server. It resolves the
|
|
56
|
+
cached manifest in your app instead of calling the backend's `/init`.
|
|
57
|
+
|
|
58
|
+
Add `initURL: '/api/c15t/init'` to your existing `defineConsentConfig` value,
|
|
59
|
+
keeping `backendURL` and `manifestURL`. Then add the handler for your router.
|
|
60
|
+
It reads the same absolute upstream backend environment variable as your
|
|
61
|
+
manifest handler.
|
|
62
|
+
|
|
63
|
+
```ts title="app/api/c15t/init/route.ts"
|
|
64
|
+
export { GET } from 'c15t/next/api';
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For Pages Router, use this file instead:
|
|
68
|
+
|
|
69
|
+
```ts title="pages/api/c15t/init.ts"
|
|
70
|
+
import { createPagesApiHandlers } from 'c15t/next/pages';
|
|
71
|
+
|
|
72
|
+
export default createPagesApiHandlers().init;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Without `initURL`, browser initialization resolves the manifest locally. It
|
|
76
|
+
uses location supplied by prefetch or your application, but cannot read your
|
|
77
|
+
host's geographic headers itself. See [geography and privacy signals](#geography-and-privacy-signals).
|
|
78
|
+
|
|
79
|
+
### Transport combinations
|
|
80
|
+
|
|
81
|
+
| Consent config | Server prefetch | Browser initialization |
|
|
82
|
+
| -------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------ |
|
|
83
|
+
| `backendURL`, `manifestURL`, `initURL` | Resolves the manifest with request inputs | Calls `initURL`, whose local handler resolves the manifest with request inputs |
|
|
84
|
+
| `backendURL`, `manifestURL` | Resolves the manifest with request inputs | Loads the manifest resolver lazily in the browser |
|
|
85
|
+
| `backendURL` only | Calls backend `/init` | Calls backend `/init` |
|
|
86
|
+
| `backendURL`, `initURL` | Calls backend `/init` | Calls `initURL` |
|
|
87
|
+
| Explicit `options.mode: offline()` | No hosted prefetch | Resolves bundled local rules |
|
|
88
|
+
|
|
89
|
+
Hosted combinations submit choices to `${backendURL}/subjects`. Offline mode
|
|
90
|
+
uses browser persistence only. A prepared server result can satisfy the first
|
|
91
|
+
browser initialization; the browser column describes what happens when
|
|
92
|
+
initialization is needed, including recovery after prefetch fails.
|
|
93
|
+
|
|
94
|
+
Setting only `initURL` does not enable server manifest resolution. Keep
|
|
95
|
+
`manifestURL` when both server and browser requests should resolve the cached
|
|
96
|
+
manifest.
|
|
97
|
+
|
|
98
|
+
### Switch to regular backend init
|
|
99
|
+
|
|
100
|
+
Remove `manifestURL` and `initURL` from `c15t.config.ts`. The
|
|
101
|
+
[browser initialization guide](../client-side.md#configure-one-backend-url)
|
|
102
|
+
has a complete backend-only configuration. For server rendering, keep the
|
|
103
|
+
prefetch and boundary wiring from your router guide. Prefetch will call backend
|
|
104
|
+
`/init` per request.
|
|
105
|
+
|
|
106
|
+
Remove unused local manifest and init handlers. If you use an optional backend
|
|
107
|
+
rewrite, removing the local init handler lets that request reach the backend
|
|
108
|
+
through the rewrite. See [optimization](../optimization.md)
|
|
109
|
+
for rewrite configuration.
|
|
110
|
+
|
|
111
|
+
## Server helper options
|
|
112
|
+
|
|
113
|
+
`resolveConsent` is the one server helper. `c15t/next/server` exports it for
|
|
114
|
+
the App Router, where the default request context reads `next/headers` and
|
|
115
|
+
calls `await connection()` from `next/server` before reading the clock.
|
|
116
|
+
`c15t/next/pages` exports the same function for the Pages Router, taking the
|
|
117
|
+
Node `req` instead. Both entries accept the options in this section; the
|
|
118
|
+
Pages Router entry replaces only the `request` adapter.
|
|
119
|
+
|
|
120
|
+
What `resolveConsent` does depends on whether it has a backend to reach. With
|
|
121
|
+
`config` or `backendURL`, it resolves policy from the manifest or backend
|
|
122
|
+
`/init` and returns the visitor's `ConsentState`. Without either, it makes no
|
|
123
|
+
network request and returns cookie- and header-only state; see
|
|
124
|
+
[cookie-only state](#cookie-only-state).
|
|
125
|
+
|
|
126
|
+
### resolveConsent options
|
|
127
|
+
|
|
128
|
+
| Option | Default | Behavior |
|
|
129
|
+
| ---------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
130
|
+
| `backendURL` | `config.backendURL` | Backend base URL. An explicit value overrides `config.backendURL`. Without a manifest source, `resolveConsent` calls `${backendURL}/init`. Omit both `backendURL` and `config` for cookie-only state. |
|
|
131
|
+
| `config` | none | A `defineConsentConfig` result supplying `backendURL` and `manifestURL`. Explicit options on this bag win over its fields. `initURL` is not read. |
|
|
132
|
+
| `manifestURL` | `config.manifestURL` | Manifest route URL. When set, server prefetch resolves policy from the manifest and does not call backend `/init`. |
|
|
133
|
+
| `manifest` | none | Inline manifest object used instead of fetching `manifestURL`. Any `manifestURL` is still resolved first, so a relative one needs a request host. |
|
|
134
|
+
| `fetch` | `globalThis.fetch` | Fetch implementation for the backend `/init` or manifest request. Wrap it in Next.js fetch caching only in manifest mode; the `/init` call carries visitor cookies and returns per-visitor state, so keep it uncached. |
|
|
135
|
+
| `forwardHeaders` | `[]` | Request header names copied onto the outgoing call. Backend `/init` also forwards `x-forwarded-for` and `user-agent` automatically; manifest requests forward only this list. Headers absent from the request are skipped. |
|
|
136
|
+
| `onError` | none | Receives the failure from the backend or manifest request. When omitted, failures are logged with `console.warn` only when `NODE_ENV` is not `production`. |
|
|
137
|
+
| `now` | `Date.now()` | Clock used to validate stored records and stamped into the result. |
|
|
138
|
+
| `cookieName` | `c15t` | Cookie holding persisted consent. Must match the client `storageConfig.storageKey`. |
|
|
139
|
+
| `country` | header detection | Overrides the country read from request headers. |
|
|
140
|
+
| `language` | header detection | Overrides the language read from `accept-language`. |
|
|
141
|
+
| `request` | `next/headers` | Request context adapter with `cookies()` and `headers()`. The default only works in the App Router. |
|
|
142
|
+
|
|
143
|
+
`resolveConsent` throws when the request adapter's `headers()` or `cookies()`
|
|
144
|
+
rejects (the default adapter rejects outside a request scope), and when your
|
|
145
|
+
`onError` callback throws. Thrown URL resolution, network and policy
|
|
146
|
+
resolution errors are handled: they return the same baseline state as the
|
|
147
|
+
cookie-only call, with stored records, geography, language and GPC from the
|
|
148
|
+
request but no resolved policy. The page still renders and the browser initializes
|
|
149
|
+
consent on mount. Non-2xx responses from backend `/init` are failures too, so
|
|
150
|
+
a `500` renders the baseline rather than throwing. A successful response whose
|
|
151
|
+
body reports `policyResolution.status: 'failed'`, for example an unsupported
|
|
152
|
+
policy contract, is different: that failed resolution is kept as the prepared
|
|
153
|
+
state, the browser does not re-initialize on mount, and the consent UI stays
|
|
154
|
+
hidden until the cause is fixed.
|
|
155
|
+
|
|
156
|
+
Absolute `http(s)` URLs are used as given. A `/`-relative `backendURL` or
|
|
157
|
+
`manifestURL` is resolved from the request: the scheme comes from
|
|
158
|
+
`x-forwarded-proto` and defaults to `https`; the host comes from
|
|
159
|
+
`x-forwarded-host`, then `host`, then the `referer` host. When no host is available, `resolveConsent` reports the error
|
|
160
|
+
and returns the baseline state. Those headers decide where the server sends
|
|
161
|
+
the request, so behind a proxy the edge must overwrite `x-forwarded-host` and
|
|
162
|
+
`x-forwarded-proto` rather than pass client values through; otherwise a client
|
|
163
|
+
can point server prefetch at a host of its choosing. When you cannot guarantee
|
|
164
|
+
that, use absolute URLs. Use `https` for any production `backendURL`: the
|
|
165
|
+
`/init` call forwards the visitor's cookies, which plain HTTP exposes on the
|
|
166
|
+
path; keep `http` for local development only.
|
|
167
|
+
|
|
168
|
+
Forwarded headers differ by path. The backend `/init` call carries the
|
|
169
|
+
request cookies, `x-forwarded-for`, `user-agent`, any `forwardHeaders`, and
|
|
170
|
+
derived `x-c15t-country`, `x-c15t-region`, `accept-language` and `sec-gpc`
|
|
171
|
+
headers; it is sent with `cache: 'no-store'` and `credentials: 'include'`.
|
|
172
|
+
The manifest request carries only the headers named in `forwardHeaders`.
|
|
173
|
+
Cookies, the client IP and the user agent are not added automatically, because
|
|
174
|
+
the manifest is public policy data, but an explicit entry is forwarded
|
|
175
|
+
verbatim: a `forwardHeaders` list that names `cookie`, `x-forwarded-for` or
|
|
176
|
+
`user-agent` sends them to the manifest host too, so do not reuse a list meant
|
|
177
|
+
for the backend path. Cookies are still read locally on both paths to restore
|
|
178
|
+
records.
|
|
179
|
+
|
|
180
|
+
An inline `manifest` is never refreshed. The manifest transport returns the
|
|
181
|
+
object as given instead of fetching it, so the snapshot is the source of truth
|
|
182
|
+
for that request rather than a cache seed. A stale snapshot resolves stale
|
|
183
|
+
policy until the application ships a new one. One request can still happen:
|
|
184
|
+
when the inline manifest has `iab.enabled: true` with an `iab.gvl` reference
|
|
185
|
+
and the matched rule uses the `iab` model, the transport fetches the Global
|
|
186
|
+
Vendor List with the `fetch` option, and a blocked network there also falls
|
|
187
|
+
back to the baseline. `backendURL` is still required because choices post to
|
|
188
|
+
`${backendURL}/subjects`.
|
|
189
|
+
|
|
190
|
+
`onError` replaces the default logging entirely. Without it, production
|
|
191
|
+
deployments render the baseline silently; pass `onError` to report failures to
|
|
192
|
+
your monitoring. `cookieName` must match the client `storageKey` for stored
|
|
193
|
+
choices to be restored at all: with a mismatch the server supplies empty
|
|
194
|
+
records, the provider treats them as prepared and skips browser hydration, so
|
|
195
|
+
the visitor's existing choice stays ignored for the whole mount, not only at
|
|
196
|
+
first paint.
|
|
197
|
+
|
|
198
|
+
### Cookie-only state
|
|
199
|
+
|
|
200
|
+
Call `resolveConsent` with no `config` and no `backendURL` when rendering
|
|
201
|
+
needs the visitor's stored records but not a resolved policy. It makes no
|
|
202
|
+
network request. It reads the request and returns a JSON-serializable
|
|
203
|
+
`ConsentState` with `initialRecords`, `initialPrivacySignals.gpc`, `now` and,
|
|
204
|
+
when any value was detected, `initialOverrides` with `country`, `region` and
|
|
205
|
+
`language`. It does not set cookies and does not cache across requests. Only
|
|
206
|
+
these options apply:
|
|
207
|
+
|
|
208
|
+
| Option | Default | Behavior |
|
|
209
|
+
| ------------ | ---------------- | --------------------------------------------------------------------------------------------------- |
|
|
210
|
+
| `now` | `Date.now()` | Clock used to validate stored records and stamped into the result. |
|
|
211
|
+
| `cookieName` | `c15t` | Cookie holding persisted consent. Must match the client `storageConfig.storageKey`. |
|
|
212
|
+
| `country` | header detection | Overrides the country read from request headers. |
|
|
213
|
+
| `language` | header detection | Overrides the language read from `accept-language`. |
|
|
214
|
+
| `request` | `next/headers` | Request context adapter with `cookies()` and `headers()`. The default only works in the App Router. |
|
|
215
|
+
|
|
216
|
+
The cookie header is read from `headers().get('cookie')` first and from
|
|
217
|
+
`request.cookies()` only when that header is absent. Country and region come
|
|
218
|
+
from the headers listed in [geography and privacy signals](#geography-and-privacy-signals).
|
|
219
|
+
|
|
220
|
+
### Types
|
|
221
|
+
|
|
222
|
+
| Type | Exported from | What it names |
|
|
223
|
+
| ---------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
224
|
+
| `ConsentState` | `c15t/next`, `c15t/next/server`, `c15t/next/pages` | The value `resolveConsent` returns and `ConsentRoot` takes as `state` |
|
|
225
|
+
| `ResolveConsentOptions` | `c15t/next/server` | The App Router options bag |
|
|
226
|
+
| `ConsentRequestOptions` | `c15t/next/server` | The request-reading subset: `now`, `cookieName`, `country`, `language`, `request` |
|
|
227
|
+
| `PagesResolveConsentOptions` | `c15t/next/pages` | `ResolveConsentOptions` with `req` in place of `request` |
|
|
228
|
+
| `ConsentRootProps` | `c15t/next` | Props of `ConsentRoot`; `ConsentRootProps['state']` also accepts the pending promise |
|
|
229
|
+
| `ConsentConfig` | `c15t/next`, `c15t/next/server`, `c15t/next/pages` | The frozen `defineConsentConfig` result passed as `config` |
|
|
230
|
+
|
|
231
|
+
### Pages Router differences
|
|
232
|
+
|
|
233
|
+
`c15t/next/pages` exports `resolveConsent` with the Node request in place of
|
|
234
|
+
the `request` adapter. `resolveConsent({ req, ...options })` accepts every
|
|
235
|
+
option in the App Router table except `request`; `req` is the request from
|
|
236
|
+
`getServerSideProps` or an API route. Omitting `config` and `backendURL`
|
|
237
|
+
returns cookie-only state here too. The result is JSON-compatible, but fields
|
|
238
|
+
that were not detected are `undefined` (for example
|
|
239
|
+
`initialPrivacySignals.gpc` without a `sec-gpc` header), and
|
|
240
|
+
`getServerSideProps` rejects `undefined` values in development. Round-trip the
|
|
241
|
+
result through `JSON.parse(JSON.stringify(result))` before returning it as a
|
|
242
|
+
prop, as `examples/nextjs/pages/pages-router.tsx` does.
|
|
243
|
+
|
|
244
|
+
`createPagesRequestContext(req)` builds the `request` adapter itself. Headers
|
|
245
|
+
are converted to Web `Headers`, and cookies are read from the `cookie` header.
|
|
246
|
+
Use it when calling the `c15t/next/server` helper from a custom server or
|
|
247
|
+
test harness where `next/headers` is unavailable:
|
|
248
|
+
|
|
249
|
+
```ts title="server/consent.ts"
|
|
250
|
+
import { resolveConsent } from 'c15t/next/server';
|
|
251
|
+
import { createPagesRequestContext } from 'c15t/next/pages';
|
|
252
|
+
import { consentConfig } from '../c15t.config';
|
|
253
|
+
import type { IncomingMessage } from 'node:http';
|
|
254
|
+
|
|
255
|
+
export function resolveRequestConsent(req: IncomingMessage) {
|
|
256
|
+
return resolveConsent({
|
|
257
|
+
config: consentConfig,
|
|
258
|
+
request: createPagesRequestContext(req),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Route handler options
|
|
264
|
+
|
|
265
|
+
### createNextConsentRouteHandlers options
|
|
266
|
+
|
|
267
|
+
`createNextConsentRouteHandlers(optionsOrConfig)` from `c15t/next/api` returns
|
|
268
|
+
`GET` for the init route and `manifestGET` for the manifest route. The
|
|
269
|
+
ready-made `GET` and `manifestGET` exports from the same module are created
|
|
270
|
+
with no options, so they read only environment variables.
|
|
271
|
+
|
|
272
|
+
| Option | Default / env | Behavior |
|
|
273
|
+
| --------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
274
|
+
| `backendURL` | `C15T_BACKEND_URL`, then `NEXT_PUBLIC_C15T_BACKEND_URL` | Backend base URL. The manifest route fetches `${backendURL}/manifest`. Not read when `manifestURL` is set. |
|
|
275
|
+
| `manifestURL` | `C15T_MANIFEST_URL` | Full upstream manifest URL. Takes precedence over `backendURL` plus `/manifest`. |
|
|
276
|
+
| `manifestRevalidateSeconds` | `300`; env `C15T_MANIFEST_REVALIDATE_SECONDS` | Next.js Data Cache revalidation for the manifest fetch. `false` disables it; the env value `'false'` also disables it. The env value is read with `parseInt`, so `60seconds` becomes `60` and `1e2` becomes `1`; values without a leading number or below `0` fall back to the default. |
|
|
277
|
+
| `fetch` | `globalThis.fetch` | Fetch implementation for the manifest and Global Vendor List requests. |
|
|
278
|
+
| `onBackgroundRevalidate` | none | Receives the promise of a background manifest refresh started by a request, so a host can keep it alive with `after` from `next/server` or a platform `waitUntil`. Called inside the handler; the promise never rejects. See [Optimization](../optimization.md#configure-manifest-cache-refresh). |
|
|
279
|
+
| `fetchGvl` | built-in cached fetcher | Loads the Global Vendor List for IAB policies. Called only under the conditions described in this section. |
|
|
280
|
+
|
|
281
|
+
An explicit option wins over its environment variable, and a manifest URL from
|
|
282
|
+
either source wins over `backendURL`. URLs are resolved per request, so a
|
|
283
|
+
missing or invalid value fails the request rather than the build: with neither
|
|
284
|
+
`backendURL` nor `manifestURL` configured the handler throws
|
|
285
|
+
`@c15t/nextjs/api: configure C15T_BACKEND_URL or C15T_MANIFEST_URL.`. A
|
|
286
|
+
`/`-relative value is resolved against the incoming request's host and
|
|
287
|
+
forwarded headers, which points the handler at your own app and lets a client
|
|
288
|
+
that controls `x-forwarded-host` choose the upstream. Keep upstream URLs
|
|
289
|
+
absolute so the manifest route neither fetches itself nor an attacker's host.
|
|
290
|
+
|
|
291
|
+
Passing a `defineConsentConfig` result uses only its `backendURL`. Its
|
|
292
|
+
`manifestURL` and `initURL` name the routes these handlers serve, so
|
|
293
|
+
forwarding them would make the manifest route fetch itself. The same rule
|
|
294
|
+
applies when the config's `backendURL` is a same-origin rewrite prefix: pass
|
|
295
|
+
the absolute upstream URL instead, as described in
|
|
296
|
+
[optimization](../optimization.md).
|
|
297
|
+
|
|
298
|
+
`GET` resolves the cached manifest with the request's geography, language and
|
|
299
|
+
GPC headers and responds with `cache-control: private, no-store` and
|
|
300
|
+
`x-c15t-policy-contract: 1`. When the request carries an
|
|
301
|
+
`x-c15t-policy-contract` header with a different value, the response keeps the
|
|
302
|
+
translations and UI data but sets `policyResolution` to `status: 'failed'`
|
|
303
|
+
with `reason: 'unsupported-contract'` and removes `policySnapshotToken` and
|
|
304
|
+
`gvl`. Requests without the header are treated as compatible.
|
|
305
|
+
|
|
306
|
+
`manifestGET` forwards the upstream `cache-control` header, or
|
|
307
|
+
`public, s-maxage=300, stale-while-revalidate=86400` when the upstream omits
|
|
308
|
+
it, plus `etag` when present and an `age` computed from the in-process cache.
|
|
309
|
+
It adds `content-type: application/json`, `x-c15t-policy-contract: 1` and
|
|
310
|
+
`x-c15t-next-revalidate`, whose value is the upstream `s-maxage` when present,
|
|
311
|
+
`300` when the upstream sends no `cache-control` at all (the default header is
|
|
312
|
+
substituted before `s-maxage` is read), and otherwise the configured
|
|
313
|
+
revalidation interval. A `language` query
|
|
314
|
+
parameter on the manifest route is passed to the upstream manifest request.
|
|
315
|
+
|
|
316
|
+
`fetchGvl` runs inside `GET` only when the manifest has `iab.enabled: true`,
|
|
317
|
+
the manifest includes an `iab.gvl` reference, and the resolved policy matched
|
|
318
|
+
with `model: 'iab'`. It receives the reference, the `fetch` option, and the
|
|
319
|
+
language taken from the first segment of the resolved translations language
|
|
320
|
+
(`en` when empty). The default fetcher caches the vendor list in process.
|
|
321
|
+
A fetched list becomes a `gvlReference` and a small banner summary in the
|
|
322
|
+
serialized payload. A `null` result keeps IAB unavailable. With
|
|
323
|
+
`config.initURL`, the browser reads the list from that same-origin handler;
|
|
324
|
+
otherwise it uses the manifest's public list URL. See
|
|
325
|
+
[server-rendered vendor data](https://c15t.com/docs/frameworks/javascript/iab/overview#server-rendered-vendor-data)
|
|
326
|
+
for loading and caching behavior.
|
|
327
|
+
|
|
328
|
+
`createPagesApiHandlers(options)` from `c15t/next/pages` accepts the same
|
|
329
|
+
options or a `defineConsentConfig` result and returns `{ init, manifest }`:
|
|
330
|
+
`init` wraps `GET` and `manifest` wraps `manifestGET`, each taking the Node
|
|
331
|
+
`req` and `res` of a `pages/api` route. Because a `pages/api` default export
|
|
332
|
+
receives every method, requests other than `GET` and `HEAD` are answered with
|
|
333
|
+
`405` and an `allow: GET` header before the wrapped handler runs.
|
|
334
|
+
|
|
335
|
+
## Manifest request resolution
|
|
336
|
+
|
|
337
|
+
The [App Router](../app-router.md) and
|
|
338
|
+
[Pages Router](../pages-router.md) manifest setups use this flow:
|
|
339
|
+
|
|
340
|
+
1. The app's manifest handler fetches the absolute backend's `/manifest`
|
|
341
|
+
endpoint and caches public policy data.
|
|
342
|
+
2. `resolveConsent({ config: consentConfig })` reads the manifest route,
|
|
343
|
+
resolves policy with the current request's inputs and restores valid consent
|
|
344
|
+
cookies.
|
|
345
|
+
3. `ConsentRoot` receives that result as `state` and the URL configuration
|
|
346
|
+
as `config`. Hydration preserves the resolved state.
|
|
347
|
+
4. If browser initialization is needed, it resolves the manifest locally. An
|
|
348
|
+
optional `initURL` moves this resolution to the app server.
|
|
349
|
+
5. Browser choices post to `${backendURL}/subjects`.
|
|
350
|
+
|
|
351
|
+
A warm policy cache avoids backend `/init` during request resolution. The app
|
|
352
|
+
may still read its manifest route, cold caches fetch upstream data, and choices
|
|
353
|
+
still reach the backend. IAB policies can also require a Global Vendor List
|
|
354
|
+
fetch. See [manifest caching](../optimization.md) for cache
|
|
355
|
+
settings, and [server rendering](../server-side.md) for awaiting
|
|
356
|
+
or streaming prefetch results.
|
|
357
|
+
|
|
358
|
+
## Geography and privacy signals
|
|
359
|
+
|
|
360
|
+
Server manifest resolution reads location headers from the hosting platform.
|
|
361
|
+
It recognizes country headers such as `cf-ipcountry` and
|
|
362
|
+
`x-vercel-ip-country`, and region headers such as `cf-region-code` and
|
|
363
|
+
`x-vercel-ip-country-region`. The application overrides `x-c15t-country` and
|
|
364
|
+
`x-c15t-region` take precedence. Only trusted infrastructure should supply
|
|
365
|
+
location overrides in production.
|
|
366
|
+
|
|
367
|
+
Request helpers also read language and GPC. Missing location stays unknown;
|
|
368
|
+
the resolver does not infer country from the Next.js server's IP. Test unknown
|
|
369
|
+
country and region against your configured policy rules.
|
|
370
|
+
|
|
371
|
+
Browser manifest resolution uses location overrides from prefetch or your
|
|
372
|
+
application. A browser-only entry without those inputs uses unknown location.
|
|
373
|
+
The [optional init route](#do-i-need-the-local-init-route) reads geographic
|
|
374
|
+
headers on the server and keeps resolver code and translations out of browser
|
|
375
|
+
initialization.
|
|
376
|
+
|
|
377
|
+
## Offline configuration
|
|
378
|
+
|
|
379
|
+
Not recommended for production environments. Use offline mode for local
|
|
380
|
+
development, tests or demos that do not need backend records.
|
|
381
|
+
|
|
382
|
+
Omit hosted prefetch and select the offline factory in a Client Component.
|
|
383
|
+
Keep your existing banner, dialog and preferences link as children of this
|
|
384
|
+
root:
|
|
385
|
+
|
|
386
|
+
```tsx title="app/offline-consent.tsx"
|
|
387
|
+
'use client';
|
|
388
|
+
|
|
389
|
+
import type { ReactNode } from 'react';
|
|
390
|
+
import { ConsentRoot, offline } from 'c15t/next';
|
|
391
|
+
|
|
392
|
+
const mode = offline();
|
|
393
|
+
|
|
394
|
+
export function OfflineConsent({ children }: { children: ReactNode }) {
|
|
395
|
+
return (
|
|
396
|
+
<ConsentRoot state={{}} options={{ mode }}>
|
|
397
|
+
{children}
|
|
398
|
+
</ConsentRoot>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
The default local policy pack handles missing geography; offline mode does not
|
|
404
|
+
perform IP lookup. Supply `policyRules` to replace that pack when your local
|
|
405
|
+
policy needs different behavior. Browser persistence stores choices, but this
|
|
406
|
+
setup has no backend record service. See
|
|
407
|
+
[transport choices](../../../guides/data-fetching.md) for the tradeoffs.
|
|
408
|
+
|
|
409
|
+
### Authenticated hosted vendor lists
|
|
410
|
+
|
|
411
|
+
When hosted `resolveConsent()` forwards cookies or additional request headers,
|
|
412
|
+
or uses a custom `fetch`, it retains the fetched vendor list in server state.
|
|
413
|
+
The browser cannot replay a private server fetch. This fallback preserves consent
|
|
414
|
+
loading and vendor filtering without copying credentials into the page. Its
|
|
415
|
+
payload size is unchanged from inline GVL loading. For compact pages with private
|
|
416
|
+
upstreams, expose the public list through a same-origin manifest init handler.
|