@c15t/nextjs 2.0.0-rc.4 → 2.0.0-rc.6

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 (87) hide show
  1. package/dist/headless.cjs +1 -1
  2. package/dist/iab/styles.css +1 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/libs/browser-initial-data.cjs +1 -0
  6. package/dist/libs/browser-initial-data.js +1 -0
  7. package/dist/libs/initial-data.cjs +1 -1
  8. package/dist/libs/initial-data.js +1 -1
  9. package/dist/styles.css +1 -0
  10. package/dist/types.cjs +1 -1
  11. package/dist/version.cjs +1 -1
  12. package/dist/version.js +1 -1
  13. package/dist-types/headless.d.ts +1 -0
  14. package/{dist → dist-types}/index.d.ts +4 -3
  15. package/dist-types/libs/browser-initial-data.d.ts +9 -0
  16. package/{dist → dist-types}/libs/initial-data.d.ts +1 -2
  17. package/dist-types/types.d.ts +38 -0
  18. package/dist-types/version.d.ts +1 -0
  19. package/docs/README.md +73 -0
  20. package/docs/building-headless-components.md +250 -0
  21. package/docs/callbacks.md +117 -0
  22. package/docs/components/consent-banner.md +174 -0
  23. package/docs/components/consent-dialog-link.md +59 -0
  24. package/docs/components/consent-dialog-trigger.md +103 -0
  25. package/docs/components/consent-dialog.md +137 -0
  26. package/docs/components/consent-manager-provider.md +423 -0
  27. package/docs/components/consent-widget.md +78 -0
  28. package/docs/components/dev-tools.md +63 -0
  29. package/docs/components/frame.md +73 -0
  30. package/docs/concepts/client-modes.md +163 -0
  31. package/docs/concepts/consent-categories.md +97 -0
  32. package/docs/concepts/consent-models.md +116 -0
  33. package/docs/concepts/cookie-management.md +122 -0
  34. package/docs/concepts/glossary.md +23 -0
  35. package/docs/concepts/initialization-flow.md +141 -0
  36. package/docs/concepts/policy-packs.md +229 -0
  37. package/docs/headless.md +184 -0
  38. package/docs/hooks/use-color-scheme.md +40 -0
  39. package/docs/hooks/use-consent-manager/checking-consent.md +94 -0
  40. package/docs/hooks/use-consent-manager/location-info.md +95 -0
  41. package/docs/hooks/use-consent-manager/overview.md +390 -0
  42. package/docs/hooks/use-consent-manager/setting-consent.md +92 -0
  43. package/docs/hooks/use-draggable.md +57 -0
  44. package/docs/hooks/use-focus-trap.md +41 -0
  45. package/docs/hooks/use-reduced-motion.md +35 -0
  46. package/docs/hooks/use-ssr-status.md +31 -0
  47. package/docs/hooks/use-text-direction.md +49 -0
  48. package/docs/hooks/use-translations.md +117 -0
  49. package/docs/iab/consent-banner.md +95 -0
  50. package/docs/iab/consent-dialog.md +135 -0
  51. package/docs/iab/overview.md +119 -0
  52. package/docs/iab/use-gvl-data.md +208 -0
  53. package/docs/iframe-blocking.md +107 -0
  54. package/docs/integrations/databuddy.md +186 -0
  55. package/docs/integrations/google-tag-manager.md +153 -0
  56. package/docs/integrations/google-tag.md +149 -0
  57. package/docs/integrations/linkedin-insights.md +109 -0
  58. package/docs/integrations/meta-pixel.md +342 -0
  59. package/docs/integrations/microsoft-uet.md +112 -0
  60. package/docs/integrations/overview.md +89 -0
  61. package/docs/integrations/posthog.md +177 -0
  62. package/docs/integrations/tiktok-pixel.md +113 -0
  63. package/docs/integrations/x-pixel.md +143 -0
  64. package/docs/internationalization.md +197 -0
  65. package/docs/network-blocker.md +178 -0
  66. package/docs/optimization.md +156 -0
  67. package/docs/policy-packs.md +246 -0
  68. package/docs/quickstart.md +155 -0
  69. package/docs/script-loader.md +300 -0
  70. package/docs/server-side.md +171 -0
  71. package/docs/styling/classnames.md +84 -0
  72. package/docs/styling/color-scheme.md +82 -0
  73. package/docs/styling/css-variables.md +92 -0
  74. package/docs/styling/overview.md +312 -0
  75. package/docs/styling/slots.md +93 -0
  76. package/docs/styling/tailwind.md +115 -0
  77. package/docs/styling/tokens.md +214 -0
  78. package/docs/troubleshooting.md +146 -0
  79. package/package.json +20 -13
  80. package/dist/headless.d.ts +0 -2
  81. package/dist/headless.d.ts.map +0 -1
  82. package/dist/index.d.ts.map +0 -1
  83. package/dist/libs/initial-data.d.ts.map +0 -1
  84. package/dist/types.d.ts +0 -16
  85. package/dist/types.d.ts.map +0 -1
  86. package/dist/version.d.ts +0 -2
  87. package/dist/version.d.ts.map +0 -1
@@ -0,0 +1,197 @@
1
+ ---
2
+ title: Internationalization
3
+ description: Translate consent UI into 30+ languages with built-in translations, custom overrides, and automatic browser language detection.
4
+ ---
5
+ c15t ships with built-in translations for 30+ languages via the `@c15t/translations` package. Language detection happens automatically based on the browser's language preference, and you can override or extend translations for any language.
6
+
7
+ In c15t v2, the preferred config shape is `i18n` with `locale`, `detectBrowserLanguage`, and `messages`.
8
+
9
+ There are two ways c15t can load translations: client-side or server-side.
10
+
11
+ |Server-side|Client-side|
12
+ |--|--|
13
+ |The best way to reduce bundle size and improve performance. We can detect the user's language based on the browser's language settings, allowing for the most accurate translations. By default, when using a [consent.io](https://consent.io) hosted instance, [these languages](https://github.com/c15t/c15t/tree/main/packages/translations/src/translations) are supported.|Bundled with the application allowing for multiple languages to be supported without the need for a backend. The more translations you have, the larger the bundle size will be, which may impact the performance of your application.|
14
+
15
+ ## Basic Configuration
16
+
17
+ Pass custom translations via the `i18n` option:
18
+
19
+ ```tsx
20
+ import { type ReactNode } from 'react';
21
+ import { ConsentManagerProvider } from '@c15t/nextjs';
22
+
23
+ export default function ConsentManager({ children }: { children: ReactNode }) {
24
+ return (
25
+ <ConsentManagerProvider
26
+ options={{
27
+ mode: 'hosted',
28
+ backendURL: '/api/c15t',
29
+ i18n: {
30
+ locale: 'en',
31
+ messages: {
32
+ de: {
33
+ cookieBanner: {
34
+ title: 'Datenschutzeinstellungen',
35
+ description: 'Wir verwenden Cookies, um Ihnen das beste Erlebnis zu bieten.',
36
+ },
37
+ common: {
38
+ acceptAll: 'Alle akzeptieren',
39
+ rejectAll: 'Alle ablehnen',
40
+ customize: 'Anpassen',
41
+ save: 'Speichern',
42
+ },
43
+ },
44
+ },
45
+ },
46
+ }}
47
+ >
48
+ {children}
49
+ </ConsentManagerProvider>
50
+ );
51
+ }
52
+ ```
53
+
54
+ Custom translations are deep-merged with built-in defaults - you only need to override the keys you want to change.
55
+
56
+ ## Translation Package Imports
57
+
58
+ Use the import path that matches your use case:
59
+
60
+ * `@c15t/translations`: types, utilities, and `enTranslations` only (smallest client bundle)
61
+ * `@c15t/translations/en`: English-only translation object
62
+ * `@c15t/translations/all`: full `baseTranslations` map for all bundled locales
63
+
64
+ If you bundle translations client-side and need multiple languages, import from `@c15t/translations/all` explicitly:
65
+
66
+ ```tsx
67
+ import { baseTranslations } from '@c15t/translations/all';
68
+
69
+ const translations = {
70
+ en: baseTranslations.en,
71
+ de: baseTranslations.de,
72
+ fr: baseTranslations.fr,
73
+ };
74
+ ```
75
+
76
+ ## Translation Sections
77
+
78
+ The `Translations` object is organized into sections:
79
+
80
+ |Section|Controls|
81
+ |--|--|
82
+ |`common`|Shared button labels: acceptAll, rejectAll, customize, save|
83
+ |`cookieBanner`|Banner title and description|
84
+ |`consentManagerDialog`|Dialog title and description|
85
+ |`consentTypes`|Per-category title and description (keyed by AllConsentNames)|
86
+ |`frame`|Frame placeholder title and button text (supports `{category}` placeholder)|
87
+ |`legalLinks`|Privacy policy, cookie policy, terms of service link text|
88
+ |`iab`|IAB TCF banner, preference center, vendor list translations|
89
+
90
+ ## Reading Translations
91
+
92
+ Use the `useTranslations()` hook to access the current language's translations:
93
+
94
+ ```tsx
95
+ import { useTranslations } from '@c15t/nextjs';
96
+
97
+ function CustomBanner() {
98
+ const translations = useTranslations();
99
+
100
+ return (
101
+ <div>
102
+ <h2>{translations.cookieBanner.title}</h2>
103
+ <p>{translations.cookieBanner.description}</p>
104
+ </div>
105
+ );
106
+ }
107
+ ```
108
+
109
+ ## Changing Language
110
+
111
+ Switch the active language at runtime with `setLanguage()`:
112
+
113
+ ```tsx
114
+ import { useConsentManager } from '@c15t/nextjs';
115
+
116
+ function LanguageSwitcher() {
117
+ const { setLanguage } = useConsentManager();
118
+
119
+ return (
120
+ <select onChange={(e) => setLanguage(e.target.value)}>
121
+ <option value="en">English</option>
122
+ <option value="de">Deutsch</option>
123
+ <option value="fr">Fran&ccedil;ais</option>
124
+ <option value="es">Espa&ntilde;ol</option>
125
+ </select>
126
+ );
127
+ }
128
+ ```
129
+
130
+ `setLanguage()` re-fetches the consent banner to get server-resolved translations for the new language.
131
+
132
+ ## Automatic Language Detection
133
+
134
+ By default, c15t detects the browser's language (`navigator.language`) and selects the closest matching translation. If custom `messages` are configured, fallback stays within your configured languages before using `locale` (or `'en'`) as the preferred fallback language.
135
+
136
+ When backend policy packs use `i18n.messageProfile`, the active language pool comes only from that resolved profile.
137
+
138
+ Example: if your Europe profile defines `en`, `fr`, and `de`, and your default profile defines `en`, `es`, and `pt`, a Europe visitor can resolve to `en`, `fr`, or `de`, but not `es`, `pt`, or `zh`.
139
+
140
+ Use profile-local `fallbackLanguage` inside backend `i18n.messages` to choose which configured language a policy profile should fall back to when the browser asks for an unsupported locale.
141
+
142
+ Disable auto-detection to always use `locale`:
143
+
144
+ ```tsx
145
+ i18n: {
146
+ locale: 'de',
147
+ detectBrowserLanguage: false,
148
+ messages: { ... },
149
+ }
150
+ ```
151
+
152
+ If you need a policy to always use one specific language regardless of browser preference, set `policy.i18n.language` on the backend policy pack.
153
+
154
+ ## Custom Consent Type Labels
155
+
156
+ Override the title and description for individual consent categories:
157
+
158
+ ```tsx
159
+ i18n: {
160
+ messages: {
161
+ en: {
162
+ consentTypes: {
163
+ measurement: {
164
+ title: 'Analytics & Performance',
165
+ description: 'Help us understand how visitors interact with our site.',
166
+ },
167
+ marketing: {
168
+ title: 'Advertising',
169
+ description: 'Used to deliver relevant ads and measure campaign effectiveness.',
170
+ },
171
+ },
172
+ },
173
+ },
174
+ }
175
+ ```
176
+
177
+ ## Legacy `translations` Compatibility
178
+
179
+ The legacy syntax is still supported in 2.0 for RC compatibility:
180
+
181
+ ```tsx
182
+ // Legacy (still supported)
183
+ translations: {
184
+ defaultLanguage: 'en',
185
+ disableAutoLanguageSwitch: true,
186
+ translations: { ... },
187
+ }
188
+
189
+ // Preferred in v2
190
+ i18n: {
191
+ locale: 'en',
192
+ detectBrowserLanguage: false,
193
+ messages: { ... },
194
+ }
195
+ ```
196
+
197
+ If both are provided, `i18n` takes precedence.
@@ -0,0 +1,178 @@
1
+ ---
2
+ title: Network Blocker
3
+ description: Block outgoing network requests to third-party domains until the user grants consent for the appropriate category.
4
+ ---
5
+ The network blocker intercepts outgoing `fetch` and `XMLHttpRequest` calls and blocks them based on consent state and domain rules. This catches tracking requests that happen outside of script loading - for example, beacon calls, API requests to analytics endpoints, or pixel fires from already-loaded scripts.
6
+
7
+ ## Configuration
8
+
9
+ Add `networkBlocker` to your provider options:
10
+
11
+ ```tsx
12
+ import { type ReactNode } from 'react';
13
+ import { ConsentManagerProvider } from '@c15t/nextjs';
14
+
15
+ export function ConsentManager({ children }: { children: ReactNode }) {
16
+ return (
17
+ <ConsentManagerProvider
18
+ options={{
19
+ mode: 'hosted',
20
+ backendURL: '/api/c15t',
21
+ networkBlocker: {
22
+ rules: [
23
+ {
24
+ id: 'google-analytics',
25
+ domain: 'google-analytics.com',
26
+ category: 'measurement',
27
+ },
28
+ {
29
+ id: 'facebook-pixel',
30
+ domain: 'facebook.com',
31
+ pathIncludes: '/tr',
32
+ category: 'marketing',
33
+ },
34
+ {
35
+ id: 'analytics-post',
36
+ domain: 'analytics.example.com',
37
+ methods: ['POST'],
38
+ category: 'measurement',
39
+ },
40
+ ],
41
+ },
42
+ }}
43
+ >
44
+ {children}
45
+ </ConsentManagerProvider>
46
+ );
47
+ }
48
+ ```
49
+
50
+ ## Rule Matching
51
+
52
+ Rules match requests using three criteria:
53
+
54
+ ### Domain matching
55
+
56
+ The `domain` field matches the request hostname. Subdomains are automatically included - a rule for `google-analytics.com` also matches `www.google-analytics.com` and `stats.google-analytics.com`.
57
+
58
+ ### Path matching
59
+
60
+ The optional `pathIncludes` field requires the request URL path to contain the specified substring. This lets you target specific endpoints without blocking the entire domain.
61
+
62
+ ### Method matching
63
+
64
+ The optional `methods` array restricts the rule to specific HTTP methods. If omitted, the rule applies to all methods.
65
+
66
+ ## Consent Conditions
67
+
68
+ Like the script loader, `category` accepts a `HasCondition`:
69
+
70
+ ```tsx
71
+ // Simple
72
+ { category: 'measurement' }
73
+
74
+ // Must have both
75
+ { category: { and: ['measurement', 'marketing'] } }
76
+
77
+ // Must have either
78
+ { category: { or: ['measurement', 'marketing'] } }
79
+ ```
80
+
81
+ ## Monitoring Blocked Requests
82
+
83
+ ### Console logging
84
+
85
+ Blocked requests are logged to the console by default. Disable with `logBlockedRequests: false`.
86
+
87
+ ### Callback
88
+
89
+ Use `onRequestBlocked` to handle blocked requests programmatically:
90
+
91
+ ```tsx
92
+ networkBlocker: {
93
+ rules: [...],
94
+ onRequestBlocked: ({ method, url, rule }) => {
95
+ console.log(`Blocked ${method} ${url} (rule: ${rule?.id})`);
96
+ },
97
+ }
98
+ ```
99
+
100
+ ## Runtime Updates
101
+
102
+ Update the network blocker configuration at runtime:
103
+
104
+ ```tsx
105
+ import { useConsentManager } from '@c15t/nextjs';
106
+
107
+ function NetworkBlockerManager() {
108
+ const { setNetworkBlocker } = useConsentManager();
109
+
110
+ const addRule = () => {
111
+ setNetworkBlocker({
112
+ rules: [
113
+ {
114
+ id: 'new-tracker',
115
+ domain: 'tracker.example.com',
116
+ category: 'marketing',
117
+ },
118
+ ],
119
+ });
120
+ };
121
+ }
122
+ ```
123
+
124
+ ## API Reference
125
+
126
+ ### NetworkBlockerRule
127
+
128
+ |Property|Type|Description|Default|Required|
129
+ |:--|:--|:--|:--|:--:|
130
+ |id|string \|undefined|Optional identifier for the rule. Useful for debugging and logging.|-|Optional|
131
+ |domain|string|Domain that this rule applies to.|-|✅ Required|
132
+ |pathIncludes|string \|undefined|Optional path substring that must be present in the request path for the rule to apply.|-|Optional|
133
+ |methods|string\[] \|undefined|Optional list of HTTP methods that this rule applies to. If omitted, the rule applies to all methods.|-|Optional|
134
+ |category|HasCondition\<AllConsentNames>|Consent condition that must be satisfied to allow the request. When this condition is not satisfied, matching requests will be blocked.|-|✅ Required|
135
+
136
+ ### NetworkBlockerConfig
137
+
138
+ |Property|Type|Description|Default|Required|
139
+ |:--|:--|:--|:--|:--:|
140
+ |enabled|boolean \|undefined|Whether the network blocker is enabled.|-|Optional|
141
+ |initialConsents|ConsentState \|undefined|The consent state snapshot that is currently used for blocking logic.|-|Optional|
142
+ |logBlockedRequests|boolean \|undefined|Whether to automatically log blocked requests to the console.|-|Optional|
143
+ |onRequestBlocked|Object \|undefined|Callback invoked whenever a request is blocked.|-|Optional|
144
+ |rules|NetworkBlockerRule|Domain rules that determine which requests should be blocked.|-|✅ Required|
145
+
146
+ #### `initialConsents` ConsentState
147
+
148
+ The consent state snapshot that is currently used for blocking logic.
149
+
150
+ |Property|Type|Description|Default|Required|
151
+ |:--|:--|:--|:--|:--:|
152
+ |experience|boolean|-|-|✅ Required|
153
+ |functionality|boolean|-|-|✅ Required|
154
+ |marketing|boolean|-|-|✅ Required|
155
+ |measurement|boolean|-|-|✅ Required|
156
+ |necessary|boolean|-|-|✅ Required|
157
+
158
+ #### `onRequestBlocked`
159
+
160
+ Callback invoked whenever a request is blocked.
161
+
162
+ |Property|Type|Description|Default|Required|
163
+ |:--|:--|:--|:--|:--:|
164
+ |method|string|The HTTP method of the blocked request (e.g. GET, POST).|-|✅ Required|
165
+ |url|string|The URL of the blocked request.|-|✅ Required|
166
+ |rule|NetworkBlockerRule \|undefined|The rule that caused the request to be blocked, if available. Can be undefined when the blocker configuration changed between evaluation and logging.|-|Optional|
167
+
168
+ #### `rules` NetworkBlockerRule
169
+
170
+ Domain rules that determine which requests should be blocked.
171
+
172
+ |Property|Type|Description|Default|Required|
173
+ |:--|:--|:--|:--|:--:|
174
+ |id|string \|undefined|Optional identifier for the rule. Useful for debugging and logging.|-|Optional|
175
+ |domain|string|Domain that this rule applies to.|-|✅ Required|
176
+ |pathIncludes|string \|undefined|Optional path substring that must be present in the request path for the rule to apply.|-|Optional|
177
+ |methods|string\[] \|undefined|Optional list of HTTP methods that this rule applies to. If omitted, the rule applies to all methods.|-|Optional|
178
+ |category|HasCondition\<AllConsentNames>|Consent condition that must be satisfied to allow the request. When this condition is not satisfied, matching requests will be blocked.|-|✅ Required|
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: Optimization
3
+ description: Improve c15t startup performance in Next.js with rewrites, static prefetching, and rendering tradeoffs.
4
+ lastModified: 2026-03-17
5
+ ---
6
+ Use this guide when you care about banner visibility speed, route static-ness, and reducing backend round-trip cost.
7
+
8
+ ## 1) Prefer Same-Origin Rewrites
9
+
10
+ Proxy c15t requests through your Next.js app so the browser calls your own origin instead of a third-party domain.
11
+
12
+ ```ts title="next.config.ts"
13
+ import type { NextConfig } from 'next';
14
+
15
+ const config: NextConfig = {
16
+ async rewrites() {
17
+ return [
18
+ {
19
+ source: '/api/c15t/:path*',
20
+ destination: `${process.env.NEXT_PUBLIC_C15T_URL}/:path*`,
21
+ },
22
+ ];
23
+ },
24
+ };
25
+
26
+ export default config;
27
+ ```
28
+
29
+ Then use:
30
+
31
+ ```tsx
32
+ <ConsentManagerProvider options={{ backendURL: '/api/c15t', mode: 'hosted' }}>
33
+ ```
34
+
35
+ Why this helps:
36
+
37
+ * Same-origin requests avoid extra DNS/TLS setup in many deployments
38
+ * Ad blockers are less likely to block your init endpoint
39
+ * You can change backend infrastructure without touching client code
40
+
41
+ > ℹ️ **Info:**
42
+ > Set NEXT\_PUBLIC\_C15T\_URL in .env to your backend URL, for example https\://your-instance.c15t.dev.
43
+ >
44
+ > ℹ️ **Info:**
45
+ > Use rewrites for browser-side calls (ConsentManagerProvider, C15tPrefetch). For server-side fetchInitialData(), prefer a direct backend URL (for example https\://your-instance.c15t.dev) to avoid an extra server proxy hop.
46
+
47
+ ## 2) Pick The Right Data Strategy
48
+
49
+ |Goal|Strategy|Tradeoff|
50
+ |--|--|--|
51
+ |Keep routes fully static|`C15tPrefetch` + `getPrefetchedInitialData()`|Still client-side fetch, but starts earlier|
52
+ |Fastest first banner on dynamic routes|`fetchInitialData()` in a Server Component (do not await)|Route becomes dynamic because of `next/headers`|
53
+ |Simplest setup|Client-only init (no prefetch)|Banner appears later on slow networks|
54
+
55
+ In production benchmarks with a same-origin rewrite, prefetching strategies show measurable improvement over client-only init:
56
+
57
+ |Strategy|Scripts loaded|Data request starts|Banner visible|
58
+ |--|--|--|--|
59
+ |Client-only (no prefetch)|baseline|baseline|baseline|
60
+ |Browser prefetch|\~1.3x faster|\~2.6x earlier|\~1.25x faster|
61
+ |Server prefetch|\~2x faster|before page loads|\~1.9x faster|
62
+
63
+ ### Static Routes: Start Fetch Early In The Browser
64
+
65
+ Use `C15tPrefetch` in your layout and consume the same Promise in the provider.
66
+
67
+ ```tsx title="app/layout.tsx"
68
+ import { C15tPrefetch } from '@c15t/nextjs';
69
+ import { ConsentManager } from '@/components/consent-manager';
70
+
71
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
72
+ return (
73
+ <html lang="en">
74
+ <head>
75
+ <C15tPrefetch
76
+ backendURL="/api/c15t"
77
+ overrides={{ country: 'DE', region: 'BE', language: 'de' }}
78
+ />
79
+ </head>
80
+ <body>
81
+ <ConsentManager>{children}</ConsentManager>
82
+ </body>
83
+ </html>
84
+ );
85
+ }
86
+ ```
87
+
88
+ ```tsx title="components/consent-manager/provider.tsx"
89
+ 'use client';
90
+
91
+ import {
92
+ ConsentManagerProvider,
93
+ ConsentBanner,
94
+ ConsentDialog,
95
+ getPrefetchedInitialData,
96
+ } from '@c15t/nextjs';
97
+
98
+ export default function ConsentManagerClient({ children }: { children: React.ReactNode }) {
99
+ return (
100
+ <ConsentManagerProvider
101
+ options={{
102
+ mode: 'hosted',
103
+ backendURL: '/api/c15t',
104
+ ssrData: getPrefetchedInitialData({
105
+ backendURL: '/api/c15t',
106
+ overrides: { country: 'DE', region: 'BE', language: 'de' },
107
+ }),
108
+ }}
109
+ >
110
+ <ConsentBanner />
111
+ <ConsentDialog />
112
+ {children}
113
+ </ConsentManagerProvider>
114
+ );
115
+ }
116
+ ```
117
+
118
+ > ℹ️ **Info:**
119
+ > C15tPrefetch uses Next.js beforeInteractive script loading, so the /init request can start before hydration. In App Router streaming output, this may appear as Next.js script bootstrap data (for example self.\_\_next\_s.push(...)) instead of a literal \<head>\<script> tag.
120
+ >
121
+ > ℹ️ **Info:**
122
+ > If you use overrides or custom credentials in C15tPrefetch, pass the same values to getPrefetchedInitialData(...) so it resolves the matching prefetched request.
123
+
124
+ ## Keep The Provider Mounted Across Navigation
125
+
126
+ Mount the consent provider at the app root so route transitions do not remount it.
127
+
128
+ Why this helps:
129
+
130
+ * Avoids re-running init work on client-side navigation
131
+ * Prevents extra callback churn from remount cycles
132
+ * Keeps banner/dialog state stable between route transitions
133
+
134
+ ## Animation Performance
135
+
136
+ The default motion tokens are tuned for speed-first product UI:
137
+
138
+ |Token|Duration|Used for|
139
+ |--|--|--|
140
+ |`fast`|80ms|Banner slide + overlay, card scale, button hover, widget entry/exit|
141
+ |`normal`|150ms|Accordion, switch toggle|
142
+ |`slow`|200ms|Dialog trigger snap, tab indicator|
143
+
144
+ These defaults follow the principle that product UI should be fast and purposeful — animations exist for spatial continuity, not decoration. In benchmarks, animation duration contributes a constant floor to "data fetched → banner visible" timing. The default tokens sit at the lower end of standard UI ranges (80-200ms) to minimize that floor.
145
+
146
+ To customize motion durations and easing, see [Styling](/docs/frameworks/next/styling/overview).
147
+
148
+ ## Reduce Network Overhead
149
+
150
+ If you must use a cross-origin backend URL, add preconnect so the browser starts DNS/TLS early:
151
+
152
+ ```tsx title="app/layout.tsx"
153
+ <head>
154
+ <link rel="preconnect" href="https://your-instance.c15t.dev" crossOrigin="" />
155
+ </head>
156
+ ```