@c15t/scripts 2.1.0 → 2.2.0-canary-20260804162155

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 (140) hide show
  1. package/AGENTS.md +62 -0
  2. package/README.md +32 -20
  3. package/dist/e2e-test-utils.cjs +51 -10
  4. package/dist/e2e-test-utils.js +37 -3
  5. package/dist/engine/compile.cjs +9 -5
  6. package/dist/engine/runtime.cjs +103 -17
  7. package/dist/engine/runtime.js +94 -12
  8. package/dist/registry.cjs +97 -12
  9. package/dist/registry.js +80 -0
  10. package/dist/resolve.cjs +9 -5
  11. package/dist/types.cjs +17 -13
  12. package/dist/vendors/_shared/attributes.cjs +13 -9
  13. package/dist/vendors/_shared/google-consent.cjs +14 -9
  14. package/dist/vendors/_shared/install-builders.cjs +12 -8
  15. package/dist/vendors/_shared/script-url.cjs +16 -12
  16. package/dist/vendors/ads-and-pixels/linkedin-insights.cjs +9 -5
  17. package/dist/vendors/ads-and-pixels/meta-pixel.cjs +13 -9
  18. package/dist/vendors/ads-and-pixels/microsoft-uet.cjs +11 -7
  19. package/dist/vendors/ads-and-pixels/reddit-pixel.cjs +9 -5
  20. package/dist/vendors/ads-and-pixels/snapchat-pixel.cjs +11 -7
  21. package/dist/vendors/ads-and-pixels/tiktok-pixel.cjs +9 -5
  22. package/dist/vendors/ads-and-pixels/x-pixel.cjs +9 -5
  23. package/dist/vendors/analytics/adobe-analytics.cjs +90 -0
  24. package/dist/vendors/analytics/adobe-analytics.js +49 -0
  25. package/dist/vendors/analytics/ahrefs-analytics.cjs +11 -7
  26. package/dist/vendors/analytics/amplitude.cjs +193 -0
  27. package/dist/vendors/analytics/amplitude.js +134 -0
  28. package/dist/vendors/analytics/clearbit.cjs +69 -0
  29. package/dist/vendors/analytics/clearbit.js +28 -0
  30. package/dist/vendors/analytics/cloudflare-web-analytics.cjs +11 -7
  31. package/dist/vendors/analytics/databuddy.cjs +11 -7
  32. package/dist/vendors/analytics/fathom-analytics.cjs +11 -7
  33. package/dist/vendors/analytics/google-tag.cjs +11 -7
  34. package/dist/vendors/analytics/heap.cjs +181 -0
  35. package/dist/vendors/analytics/heap.js +134 -0
  36. package/dist/vendors/analytics/hightouch.cjs +153 -0
  37. package/dist/vendors/analytics/hightouch.js +109 -0
  38. package/dist/vendors/analytics/hotjar.cjs +11 -7
  39. package/dist/vendors/analytics/logrocket.cjs +99 -0
  40. package/dist/vendors/analytics/logrocket.js +58 -0
  41. package/dist/vendors/analytics/matomo-analytics.cjs +9 -5
  42. package/dist/vendors/analytics/microsoft-clarity.cjs +75 -47
  43. package/dist/vendors/analytics/microsoft-clarity.js +64 -40
  44. package/dist/vendors/analytics/mixpanel-analytics.cjs +31 -16
  45. package/dist/vendors/analytics/mixpanel-analytics.js +22 -11
  46. package/dist/vendors/analytics/pirsch.cjs +108 -0
  47. package/dist/vendors/analytics/pirsch.js +67 -0
  48. package/dist/vendors/analytics/plausible-analytics.cjs +9 -5
  49. package/dist/vendors/analytics/posthog.cjs +63 -27
  50. package/dist/vendors/analytics/posthog.js +52 -20
  51. package/dist/vendors/analytics/promptwatch.cjs +11 -7
  52. package/dist/vendors/analytics/rudderstack.cjs +227 -0
  53. package/dist/vendors/analytics/rudderstack.js +183 -0
  54. package/dist/vendors/analytics/rybbit-analytics.cjs +9 -5
  55. package/dist/vendors/analytics/segment.cjs +9 -5
  56. package/dist/vendors/analytics/umami-analytics.cjs +9 -5
  57. package/dist/vendors/analytics/vercel-analytics.cjs +11 -7
  58. package/dist/vendors/functional/crisp.cjs +9 -5
  59. package/dist/vendors/functional/intercom.cjs +11 -7
  60. package/dist/vendors/tag-managers/google-tag-manager.cjs +11 -7
  61. package/dist-types/registry.d.ts +313 -241
  62. package/dist-types/types.d.ts +46 -4
  63. package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +18 -18
  64. package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +21 -21
  65. package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +32 -32
  66. package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +19 -19
  67. package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +16 -16
  68. package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +28 -28
  69. package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +13 -13
  70. package/dist-types/vendors/analytics/adobe-analytics.d.ts +75 -0
  71. package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +7 -7
  72. package/dist-types/vendors/analytics/amplitude.d.ts +234 -0
  73. package/dist-types/vendors/analytics/clearbit.d.ts +60 -0
  74. package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +7 -7
  75. package/dist-types/vendors/analytics/databuddy.d.ts +25 -25
  76. package/dist-types/vendors/analytics/fathom-analytics.d.ts +11 -11
  77. package/dist-types/vendors/analytics/google-tag.d.ts +17 -17
  78. package/dist-types/vendors/analytics/heap.d.ts +316 -0
  79. package/dist-types/vendors/analytics/hightouch.d.ts +285 -0
  80. package/dist-types/vendors/analytics/hotjar.d.ts +14 -14
  81. package/dist-types/vendors/analytics/logrocket.d.ts +101 -0
  82. package/dist-types/vendors/analytics/microsoft-clarity.d.ts +42 -30
  83. package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +40 -28
  84. package/dist-types/vendors/analytics/pirsch.d.ts +96 -0
  85. package/dist-types/vendors/analytics/plausible-analytics.d.ts +13 -13
  86. package/dist-types/vendors/analytics/posthog.d.ts +63 -45
  87. package/dist-types/vendors/analytics/promptwatch.d.ts +7 -7
  88. package/dist-types/vendors/analytics/rudderstack.d.ts +330 -0
  89. package/dist-types/vendors/analytics/rybbit-analytics.d.ts +18 -18
  90. package/dist-types/vendors/analytics/segment.d.ts +13 -13
  91. package/dist-types/vendors/analytics/umami-analytics.d.ts +12 -12
  92. package/dist-types/vendors/analytics/vercel-analytics.d.ts +16 -16
  93. package/dist-types/vendors/functional/crisp.d.ts +11 -11
  94. package/dist-types/vendors/functional/intercom.d.ts +16 -16
  95. package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +18 -18
  96. package/docs/README.md +62 -0
  97. package/docs/frameworks/javascript/script-loader.md +356 -0
  98. package/docs/frameworks/next/script-loader.md +501 -0
  99. package/docs/frameworks/react/script-loader.md +555 -0
  100. package/docs/integrations/adobe-analytics.md +160 -0
  101. package/docs/integrations/ahrefs-analytics.md +157 -0
  102. package/docs/integrations/amplitude.md +230 -0
  103. package/docs/integrations/building-integrations.md +239 -0
  104. package/docs/integrations/clearbit.md +130 -0
  105. package/docs/integrations/cloudflare-web-analytics.md +126 -0
  106. package/docs/integrations/crisp.md +140 -0
  107. package/docs/integrations/databuddy.md +205 -0
  108. package/docs/integrations/fathom-analytics.md +150 -0
  109. package/docs/integrations/google-maps.md +263 -0
  110. package/docs/integrations/google-tag-manager.md +154 -0
  111. package/docs/integrations/google-tag.md +134 -0
  112. package/docs/integrations/heap.md +219 -0
  113. package/docs/integrations/hightouch.md +199 -0
  114. package/docs/integrations/hotjar.md +143 -0
  115. package/docs/integrations/intercom.md +144 -0
  116. package/docs/integrations/linkedin-insights.md +161 -0
  117. package/docs/integrations/logrocket.md +175 -0
  118. package/docs/integrations/matomo-analytics.md +171 -0
  119. package/docs/integrations/meta-pixel.md +412 -0
  120. package/docs/integrations/microsoft-clarity.md +173 -0
  121. package/docs/integrations/microsoft-uet.md +156 -0
  122. package/docs/integrations/mixpanel-analytics.md +130 -0
  123. package/docs/integrations/overview.md +149 -0
  124. package/docs/integrations/pirsch.md +142 -0
  125. package/docs/integrations/plausible-analytics.md +154 -0
  126. package/docs/integrations/posthog.md +258 -0
  127. package/docs/integrations/promptwatch.md +121 -0
  128. package/docs/integrations/reddit-pixel.md +241 -0
  129. package/docs/integrations/rudderstack.md +271 -0
  130. package/docs/integrations/rybbit-analytics.md +143 -0
  131. package/docs/integrations/segment.md +146 -0
  132. package/docs/integrations/snapchat-pixel.md +163 -0
  133. package/docs/integrations/tiktok-pixel.md +123 -0
  134. package/docs/integrations/umami-analytics.md +148 -0
  135. package/docs/integrations/vercel-analytics.md +141 -0
  136. package/docs/integrations/x-pixel.md +157 -0
  137. package/docs/integrations/youtube.md +193 -0
  138. package/docs/shared/react/guides/script-loader.md +311 -0
  139. package/package.json +61 -7
  140. package/readme.json +8 -4
@@ -0,0 +1,141 @@
1
+ ---
2
+ title: Vercel Analytics
3
+ description: Bootstrap Vercel Analytics with a declarative queue and script attributes.
4
+ group: integrations
5
+ icon: vercel-analytics
6
+ ---
7
+ Vercel Analytics loads through `@c15t/scripts` using a declarative queue bootstrap (`vaq` + `va`) and script attributes for DSN/endpoint options.
8
+
9
+ ## Integrate with c15t
10
+
11
+ **React**
12
+
13
+ ```tsx
14
+ import { type ReactNode } from 'react';
15
+ import { ConsentManagerProvider } from '@c15t/react';
16
+ import { vercelAnalytics } from '@c15t/scripts/vercel-analytics';
17
+
18
+ const scripts = [vercelAnalytics()];
19
+
20
+ export function ConsentProvider({ children }: { children: ReactNode }) {
21
+ return (
22
+ <ConsentManagerProvider
23
+ options={{
24
+ mode: 'hosted',
25
+ backendURL: 'https://your-instance.c15t.dev',
26
+ scripts,
27
+ }}
28
+ >
29
+ {children}
30
+ </ConsentManagerProvider>
31
+ );
32
+ }
33
+ ```
34
+
35
+ **Next.js**
36
+
37
+ ```tsx
38
+ 'use client';
39
+
40
+ import { type ReactNode } from 'react';
41
+ import { ConsentManagerProvider } from '@c15t/nextjs';
42
+ import { vercelAnalytics } from '@c15t/scripts/vercel-analytics';
43
+
44
+ const scripts = [vercelAnalytics()];
45
+
46
+ export function ConsentProvider({ children }: { children: ReactNode }) {
47
+ return (
48
+ <ConsentManagerProvider
49
+ options={{
50
+ mode: 'hosted',
51
+ backendURL: '/api/c15t',
52
+ scripts,
53
+ }}
54
+ >
55
+ {children}
56
+ </ConsentManagerProvider>
57
+ );
58
+ }
59
+ ```
60
+
61
+ **JavaScript**
62
+
63
+ ```ts
64
+ import { getOrCreateConsentRuntime } from 'c15t';
65
+ import { vercelAnalytics } from '@c15t/scripts/vercel-analytics';
66
+
67
+ getOrCreateConsentRuntime({
68
+ mode: 'hosted',
69
+ backendURL: 'https://your-instance.c15t.dev',
70
+ scripts: [vercelAnalytics()],
71
+ });
72
+ ```
73
+
74
+ ## How c15t loads it
75
+
76
+ * **Category:** `measurement` (Analytics)
77
+ * **Loads when:** measurement consent is granted
78
+ * **On revocation:** unloaded - c15t removes the script from the DOM and clears access to runtime globals until consent is granted again.
79
+
80
+ Load the debug bundle or a custom endpoint when needed:
81
+
82
+ ```ts
83
+ vercelAnalytics({
84
+ mode: 'development',
85
+ disableAutoTrack: true,
86
+ endpoint: 'https://analytics.example.com/v1/events',
87
+ })
88
+ ```
89
+
90
+ ## Tracking events in your app
91
+
92
+ c15t initializes a bootstrap phase first (`vaq` plus a local `window.va` stub), then loads the remote Vercel Analytics script after `measurement` consent is granted. Calls are safe once the bootstrap stub exists (they are queued/ignored in `vaq`), but they execute only after consent is granted and the real `window.va` is injected and flushes the queue.
93
+
94
+ Guard event calls by checking consent state. From React:
95
+
96
+ ```tsx
97
+ import { useCallback } from 'react';
98
+ import { useConsentManager } from '@c15t/react';
99
+
100
+ function SignupExample() {
101
+ const { has } = useConsentManager();
102
+
103
+ const trackSignup = useCallback(() => {
104
+ if (has('measurement')) {
105
+ window.va?.('signup', { plan: 'pro' });
106
+ }
107
+ }, [has]);
108
+ }
109
+ ```
110
+
111
+ From plain JavaScript:
112
+
113
+ ```ts
114
+ import { getOrCreateConsentRuntime } from 'c15t';
115
+
116
+ const { consentStore } = getOrCreateConsentRuntime();
117
+
118
+ if (consentStore.getState().has('measurement')) {
119
+ window.va?.('signup', { plan: 'pro' });
120
+ }
121
+ ```
122
+
123
+ ## Types
124
+
125
+ ### VercelAnalyticsOptions
126
+
127
+ |Property|Value|
128
+ |:--|:--|
129
+ |Type Name|\`VercelAnalyticsOptions\`|
130
+ |Source Path|\`./packages/scripts/src/vendors/analytics/vercel-analytics.ts\`|
131
+
132
+ \*ExtractedTypeTable: Could not extract "VercelAnalyticsOptions" from "./packages/scripts/src/vendors/analytics/vercel-analytics.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
133
+
134
+ ### Script
135
+
136
+ |Property|Value|
137
+ |:--|:--|
138
+ |Type Name|\`Script\`|
139
+ |Source Path|\`./packages/core/src/libs/script-loader/types.ts\`|
140
+
141
+ \*ExtractedTypeTable: Could not extract "Script" from "./packages/core/src/libs/script-loader/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
@@ -0,0 +1,157 @@
1
+ ---
2
+ title: X Pixel (Twitter Pixel)
3
+ description: Track conversions and build audiences for advertising campaigns on
4
+ X (formerly Twitter).
5
+ icon: x
6
+ group: integrations
7
+ ---
8
+ X Pixel (formerly Twitter Pixel) is X's conversion tracking and audience building tool. It helps you measure ad performance, retarget website visitors, and optimize campaigns on the X platform.
9
+
10
+ ## Official X documentation
11
+
12
+ * [Conversion tracking for websites (X Ads Help)](https://business.x.com/en/help/campaign-measurement-and-analytics/conversion-tracking-for-websites)
13
+
14
+ ## Integrate with c15t
15
+
16
+ **React**
17
+
18
+ ```tsx
19
+ import { type ReactNode } from 'react';
20
+ import { ConsentManagerProvider } from '@c15t/react';
21
+ import { xPixel } from '@c15t/scripts/x-pixel';
22
+
23
+ const scripts = [xPixel({ pixelId: '123456789012345' })];
24
+
25
+ export function ConsentProvider({ children }: { children: ReactNode }) {
26
+ return (
27
+ <ConsentManagerProvider
28
+ options={{
29
+ mode: 'hosted',
30
+ backendURL: 'https://your-instance.c15t.dev',
31
+ scripts,
32
+ }}
33
+ >
34
+ {children}
35
+ </ConsentManagerProvider>
36
+ );
37
+ }
38
+ ```
39
+
40
+ **Next.js**
41
+
42
+ ```tsx
43
+ 'use client';
44
+
45
+ import { type ReactNode } from 'react';
46
+ import { ConsentManagerProvider } from '@c15t/nextjs';
47
+ import { xPixel } from '@c15t/scripts/x-pixel';
48
+
49
+ const scripts = [xPixel({ pixelId: '123456789012345' })];
50
+
51
+ export function ConsentProvider({ children }: { children: ReactNode }) {
52
+ return (
53
+ <ConsentManagerProvider
54
+ options={{
55
+ mode: 'hosted',
56
+ backendURL: '/api/c15t',
57
+ scripts,
58
+ }}
59
+ >
60
+ {children}
61
+ </ConsentManagerProvider>
62
+ );
63
+ }
64
+ ```
65
+
66
+ **JavaScript**
67
+
68
+ ```ts
69
+ import { getOrCreateConsentRuntime } from 'c15t';
70
+ import { xPixel } from '@c15t/scripts/x-pixel';
71
+
72
+ getOrCreateConsentRuntime({
73
+ mode: 'hosted',
74
+ backendURL: 'https://your-instance.c15t.dev',
75
+ scripts: [xPixel({ pixelId: '123456789012345' })],
76
+ });
77
+ ```
78
+
79
+ ## How c15t loads it
80
+
81
+ * **Category:** `marketing` (Ads & Pixels)
82
+ * **Loads when:** marketing consent is granted
83
+ * **On revocation:** unloaded — c15t removes the script element from the DOM
84
+
85
+ ## Configure the integration
86
+
87
+ You can use the `xPixelEvent` function to track events. This is a wrapper around the `twq` function that the X Pixel script uses.
88
+
89
+ ```ts
90
+ import { xPixelEvent } from '@c15t/scripts/x-pixel';
91
+
92
+ xPixelEvent('tw-xxxx-xxxx', { value: 10.00, currency: 'USD' });
93
+ ```
94
+
95
+ ## Tracking events in your app
96
+
97
+ c15t gates the X Pixel script from loading until `marketing` consent is granted. Your application code that calls `twq` or the `xPixelEvent` helper is **not** automatically gated — `window.twq` does not exist before consent is granted, and is removed again if consent is revoked. Unguarded calls throw.
98
+
99
+ Guard event calls by checking consent state:
100
+
101
+ ```tsx
102
+ import { useCallback } from 'react';
103
+ import { useConsentManager } from '@c15t/react';
104
+ import { xPixelEvent } from '@c15t/scripts/x-pixel';
105
+
106
+ function useTrackPurchase() {
107
+ const { has } = useConsentManager();
108
+ return useCallback(() => {
109
+ if (has('marketing')) {
110
+ xPixelEvent('tw-xxxx-xxxx', { value: 10.0, currency: 'USD' });
111
+ }
112
+ }, [has]);
113
+ }
114
+
115
+ function PurchaseButton() {
116
+ const trackPurchase = useTrackPurchase();
117
+ return <button onClick={trackPurchase}>Complete purchase</button>;
118
+ }
119
+ ```
120
+
121
+ ## Types
122
+
123
+ ### XPixelOptions
124
+
125
+ |Property|Value|
126
+ |:--|:--|
127
+ |Type Name|\`XPixelOptions\`|
128
+ |Source Path|\`./packages/scripts/src/vendors/ads-and-pixels/x-pixel.ts\`|
129
+
130
+ \*ExtractedTypeTable: Could not extract "XPixelOptions" from "./packages/scripts/src/vendors/ads-and-pixels/x-pixel.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
131
+
132
+ ### Script
133
+
134
+ |Property|Value|
135
+ |:--|:--|
136
+ |Type Name|\`Script\`|
137
+ |Source Path|\`./packages/core/src/libs/script-loader/types.ts\`|
138
+
139
+ \*ExtractedTypeTable: Could not extract "Script" from "./packages/core/src/libs/script-loader/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
140
+
141
+ ### XPixelEvent
142
+
143
+ |Property|Value|
144
+ |:--|:--|
145
+ |Type Name|\`XPixelEvent\`|
146
+ |Source Path|\`./packages/scripts/src/vendors/ads-and-pixels/x-pixel.ts\`|
147
+
148
+ \*ExtractedTypeTable: Could not extract "XPixelEvent" from "./packages/scripts/src/vendors/ads-and-pixels/x-pixel.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
149
+
150
+ ### XPixelContent
151
+
152
+ |Property|Value|
153
+ |:--|:--|
154
+ |Type Name|\`XPixelContent\`|
155
+ |Source Path|\`./packages/scripts/src/vendors/ads-and-pixels/x-pixel.ts\`|
156
+
157
+ \*ExtractedTypeTable: Could not extract "XPixelContent" from "./packages/scripts/src/vendors/ads-and-pixels/x-pixel.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
@@ -0,0 +1,193 @@
1
+ ---
2
+ title: YouTube
3
+ description: Keep YouTube iframes unmounted until consent with a
4
+ privacy-enhanced, lazy-loaded React or Next.js embed.
5
+ icon: youtube
6
+ group: integrations
7
+ ---
8
+ `YouTubeEmbed` is a renderable integration for React and Next.js. It uses c15t's
9
+ `Frame` consent boundary, so the YouTube iframe is not mounted and no YouTube
10
+ request is made until the configured consent category is allowed.
11
+
12
+ The component does not load the YouTube IFrame Player API. It is intended for
13
+ standard iframe embeds and keeps script readiness separate from iframe gating.
14
+
15
+ ## Integrate with c15t
16
+
17
+ `YouTubeEmbed` must render inside a `ConsentManagerProvider`. Complete the
18
+ [React quickstart](/docs/frameworks/react/quickstart) or
19
+ [Next.js quickstart](/docs/frameworks/next/quickstart) first.
20
+
21
+ **React**
22
+
23
+ ```tsx
24
+ import { YouTubeEmbed } from '@c15t/react';
25
+
26
+ export function ProductVideo() {
27
+ return (
28
+ <YouTubeEmbed
29
+ consentCategory="marketing"
30
+ params={{ controls: true, playsinline: true }}
31
+ title="Product overview"
32
+ videoId="dQw4w9WgXcQ"
33
+ />
34
+ );
35
+ }
36
+ ```
37
+
38
+ **Next.js**
39
+
40
+ ```tsx
41
+ 'use client';
42
+
43
+ import { YouTubeEmbed } from '@c15t/nextjs';
44
+
45
+ export function ProductVideo() {
46
+ return (
47
+ <YouTubeEmbed
48
+ consentCategory="marketing"
49
+ params={{ controls: true, playsinline: true }}
50
+ title="Product overview"
51
+ videoId="dQw4w9WgXcQ"
52
+ />
53
+ );
54
+ }
55
+ ```
56
+
57
+ `marketing` is the default category. Choose a different category only when it
58
+ matches the purpose of the embed and the policy presented to your users.
59
+
60
+ ## How c15t loads it
61
+
62
+ * **Before consent:** c15t renders a placeholder and does not mount the iframe.
63
+ * **After consent:** the iframe mounts with its final embed URL.
64
+ * **On revocation:** the `Frame` boundary unmounts the iframe, stopping the
65
+ embedded player and future requests from that document.
66
+ * **Loading behavior:** c15t reserves the final player size, shows a localized
67
+ loading state, and uses native iframe lazy loading by default.
68
+ * **Default layout:** the placeholder and player share a responsive 16:9,
69
+ borderless frame with a `200px` minimum height.
70
+ * **Privacy-enhanced mode:** URLs built from `videoId` use
71
+ `youtube-nocookie.com` by default.
72
+
73
+ Privacy-enhanced mode changes the YouTube host but does not replace consent
74
+ gating. Keep the iframe behind the category required by your privacy policy.
75
+
76
+ ## Build the embed URL
77
+
78
+ Prefer `videoId` when you control the video:
79
+
80
+ ```tsx
81
+ <YouTubeEmbed
82
+ consentCategory="marketing"
83
+ params={{
84
+ autoplay: false,
85
+ controls: true,
86
+ playsinline: true,
87
+ rel: false,
88
+ }}
89
+ start={36}
90
+ title="Quarterly product update"
91
+ videoId="dQw4w9WgXcQ"
92
+ />
93
+ ```
94
+
95
+ Boolean `params` are serialized as YouTube's `1` and `0` values. `start` is
96
+ serialized as the player's start time in seconds. If `params.start` is also
97
+ present, the top-level `start` prop takes precedence.
98
+
99
+ Set `privacyEnhanced={false}` only when you intentionally need the regular
100
+ `youtube.com` host.
101
+
102
+ ### Migrate an existing iframe URL
103
+
104
+ Use `src` when you already have a complete embed URL:
105
+
106
+ ```tsx
107
+ <YouTubeEmbed
108
+ consentCategory="marketing"
109
+ src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ?start=36"
110
+ title="Quarterly product update"
111
+ />
112
+ ```
113
+
114
+ When `src` is provided, c15t uses it unchanged. TypeScript treats `src` and
115
+ `videoId` as mutually exclusive source modes: `start`, `params`, and
116
+ `privacyEnhanced` are available only with `videoId`.
117
+
118
+ ## Style the wrapper and iframe
119
+
120
+ `wrapperClassName` targets the consent-gated `Frame`. `className` and the
121
+ forwarded ref target the iframe itself.
122
+
123
+ The defaults are responsive and stable without utility classes. Override them
124
+ only when your layout needs a different aspect ratio or height:
125
+
126
+ ```tsx
127
+ <YouTubeEmbed
128
+ consentCategory="marketing"
129
+ frameProps={{ style: { aspectRatio: '4 / 3', minHeight: 280 } }}
130
+ style={{ borderRadius: 4 }}
131
+ title="Product overview"
132
+ videoId="dQw4w9WgXcQ"
133
+ />
134
+ ```
135
+
136
+ `frameProps.style` is merged after the wrapper defaults. The iframe fills that
137
+ wrapper, has no border, and inherits the wrapper radius by default.
138
+
139
+ ## Customize the placeholder
140
+
141
+ ```tsx
142
+ import { Frame, YouTubeEmbed } from '@c15t/react';
143
+
144
+ <YouTubeEmbed
145
+ consentCategory="marketing"
146
+ placeholder={
147
+ <Frame.Root>
148
+ <Frame.Title>Allow marketing consent to watch this video.</Frame.Title>
149
+ <Frame.Button category="marketing" />
150
+ </Frame.Root>
151
+ }
152
+ title="Product overview"
153
+ videoId="dQw4w9WgXcQ"
154
+ />
155
+ ```
156
+
157
+ Custom placeholders should explain why the content is blocked and provide a
158
+ clear way to change consent. Always give the iframe a meaningful `title`.
159
+
160
+ Use `loadingFallback` to replace the post-consent loading message and
161
+ `errorFallback` to replace the configuration error state. If neither `videoId`
162
+ nor `src` is supplied at runtime, the component renders that error state instead
163
+ of mounting an iframe.
164
+
165
+ Standard cross-origin iframes do not provide a reliable player-error signal.
166
+ `YouTubeEmbed` forwards the iframe's native `onError` when a browser emits it,
167
+ but player-level errors require the YouTube IFrame Player API and are outside
168
+ this iframe-only component.
169
+
170
+ ## Verify setup
171
+
172
+ 1. Clear saved consent and reload the page.
173
+ 2. Confirm there is no YouTube iframe or YouTube network request before consent.
174
+ 3. Grant the configured category and confirm exactly one iframe mounts.
175
+ 4. Confirm a `videoId` embed uses `youtube-nocookie.com` unless
176
+ `privacyEnhanced={false}`.
177
+ 5. Revoke consent and confirm the iframe is removed.
178
+ 6. Confirm the default 16:9 frame is borderless and reserves the same space at
179
+ mobile and desktop widths.
180
+
181
+ See YouTube's [embedded player parameters](https://developers.google.com/youtube/player_parameters)
182
+ for the supported query parameters.
183
+
184
+ ## Types
185
+
186
+ ### YouTubeEmbedProps
187
+
188
+ |Property|Value|
189
+ |:--|:--|
190
+ |Type Name|\`YouTubeEmbedProps\`|
191
+ |Source Path|\`./packages/react/src/components/integrations/youtube-embed.tsx\`|
192
+
193
+ \*ExtractedTypeTable: Could not extract "YouTubeEmbedProps" from "./packages/react/src/components/integrations/youtube-embed.tsx" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*