@c15t/scripts 3.0.0-alpha.2 → 3.0.0-alpha.4
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 +129 -63
- package/README.md +8 -29
- package/SKILL.md +33 -0
- package/dist/adobe-analytics.js +2 -0
- package/dist/ahrefs-analytics.js +2 -0
- package/dist/amplitude.js +2 -0
- package/dist/clearbit.js +2 -0
- package/dist/cloudflare-web-analytics.js +2 -0
- package/dist/cloudflare-zaraz.js +2 -0
- package/dist/crisp.js +2 -0
- package/dist/databuddy.js +2 -0
- package/dist/e2e-test-utils.js +2 -139
- package/dist/engine/compile.js +2 -89
- package/dist/engine/runtime.js +2 -448
- package/dist/events.js +2 -218
- package/dist/fathom-analytics.js +2 -0
- package/dist/front-chat.js +2 -0
- package/dist/google-tag-manager.js +2 -0
- package/dist/google-tag.js +2 -0
- package/dist/heap.js +2 -0
- package/dist/hightouch.js +2 -0
- package/dist/hotjar.js +2 -0
- package/dist/intercom.js +2 -0
- package/dist/klaviyo.js +2 -0
- package/dist/linkedin-insights.js +2 -0
- package/dist/logrocket.js +2 -0
- package/dist/matomo-analytics.js +2 -0
- package/dist/meta-pixel.js +2 -0
- package/dist/microsoft-clarity.js +2 -0
- package/dist/microsoft-uet.js +2 -0
- package/dist/mixpanel-analytics.js +2 -0
- package/dist/one-dollar-stats.js +2 -0
- package/dist/openai-pixel.js +2 -0
- package/dist/pinterest-tag.js +2 -0
- package/dist/pirsch.js +2 -0
- package/dist/plausible-analytics.js +2 -0
- package/dist/posthog.js +2 -0
- package/dist/promptwatch.js +2 -0
- package/dist/reddit-pixel.js +2 -0
- package/dist/registry.js +2 -422
- package/dist/resolve.js +2 -33
- package/dist/rudderstack.js +2 -0
- package/dist/rybbit-analytics.js +2 -0
- package/dist/segment.js +2 -0
- package/dist/snapchat-pixel.js +2 -0
- package/dist/tiktok-pixel.js +2 -0
- package/dist/types.js +2 -16
- package/dist/umami-analytics.js +2 -0
- package/dist/vendors/_shared/attributes.js +2 -14
- package/dist/vendors/_shared/google-consent.js +2 -27
- package/dist/vendors/_shared/install-builders.js +2 -21
- package/dist/vendors/_shared/required-id.js +2 -0
- package/dist/vendors/_shared/script-url.js +2 -28
- package/dist/vendors/ads-and-pixels/linkedin-insights.js +2 -48
- package/dist/vendors/ads-and-pixels/meta-pixel.js +2 -153
- package/dist/vendors/ads-and-pixels/microsoft-uet.js +2 -110
- package/dist/vendors/ads-and-pixels/openai-pixel.js +2 -88
- package/dist/vendors/ads-and-pixels/pinterest-tag.js +2 -123
- package/dist/vendors/ads-and-pixels/reddit-pixel.js +2 -107
- package/dist/vendors/ads-and-pixels/snapchat-pixel.js +2 -87
- package/dist/vendors/ads-and-pixels/tiktok-pixel.js +2 -89
- package/dist/vendors/ads-and-pixels/x-pixel.js +2 -48
- package/dist/vendors/analytics/adobe-analytics.js +2 -49
- package/dist/vendors/analytics/ahrefs-analytics.js +2 -27
- package/dist/vendors/analytics/amplitude.js +2 -134
- package/dist/vendors/analytics/clearbit.js +2 -28
- package/dist/vendors/analytics/cloudflare-web-analytics.js +2 -32
- package/dist/vendors/analytics/databuddy.js +2 -103
- package/dist/vendors/analytics/fathom-analytics.js +2 -35
- package/dist/vendors/analytics/google-tag.js +2 -78
- package/dist/vendors/analytics/heap.js +2 -134
- package/dist/vendors/analytics/hightouch.js +2 -109
- package/dist/vendors/analytics/hotjar.js +2 -44
- package/dist/vendors/analytics/logrocket.js +2 -58
- package/dist/vendors/analytics/matomo-analytics.js +2 -191
- package/dist/vendors/analytics/microsoft-clarity.js +2 -100
- package/dist/vendors/analytics/mixpanel-analytics.js +2 -93
- package/dist/vendors/analytics/one-dollar-stats.js +2 -30
- package/dist/vendors/analytics/pirsch.js +2 -67
- package/dist/vendors/analytics/plausible-analytics.js +2 -81
- package/dist/vendors/analytics/posthog.js +2 -200
- package/dist/vendors/analytics/promptwatch.js +2 -29
- package/dist/vendors/analytics/rudderstack.js +2 -183
- package/dist/vendors/analytics/rybbit-analytics.js +2 -63
- package/dist/vendors/analytics/segment.js +2 -65
- package/dist/vendors/analytics/umami-analytics.js +2 -39
- package/dist/vendors/analytics/vercel-analytics.js +2 -53
- package/dist/vendors/email-and-sms/klaviyo.js +2 -0
- package/dist/vendors/functional/crisp.js +2 -100
- package/dist/vendors/functional/front-chat.js +2 -64
- package/dist/vendors/functional/intercom.js +2 -45
- package/dist/vendors/tag-managers/cloudflare-zaraz.js +2 -98
- package/dist/vendors/tag-managers/google-tag-manager.js +2 -73
- package/dist/vercel-analytics.js +2 -0
- package/dist/x-pixel.js +2 -0
- package/dist-types/adobe-analytics.d.ts +2 -0
- package/dist-types/ahrefs-analytics.d.ts +2 -0
- package/dist-types/amplitude.d.ts +2 -0
- package/dist-types/clearbit.d.ts +2 -0
- package/dist-types/cloudflare-web-analytics.d.ts +2 -0
- package/dist-types/cloudflare-zaraz.d.ts +2 -0
- package/dist-types/crisp.d.ts +2 -0
- package/dist-types/databuddy.d.ts +2 -0
- package/dist-types/e2e-test-utils.d.ts +2 -0
- package/dist-types/engine/compile.d.ts +2 -3
- package/dist-types/engine/runtime.d.ts +2 -3
- package/dist-types/events.d.ts +2 -46
- package/dist-types/fathom-analytics.d.ts +2 -0
- package/dist-types/front-chat.d.ts +2 -0
- package/dist-types/google-tag-manager.d.ts +2 -0
- package/dist-types/google-tag.d.ts +2 -0
- package/dist-types/heap.d.ts +2 -0
- package/dist-types/hightouch.d.ts +2 -0
- package/dist-types/hotjar.d.ts +2 -0
- package/dist-types/intercom.d.ts +2 -0
- package/dist-types/klaviyo.d.ts +2 -0
- package/dist-types/linkedin-insights.d.ts +2 -0
- package/dist-types/logrocket.d.ts +2 -0
- package/dist-types/matomo-analytics.d.ts +2 -0
- package/dist-types/meta-pixel.d.ts +2 -0
- package/dist-types/microsoft-clarity.d.ts +2 -0
- package/dist-types/microsoft-uet.d.ts +2 -0
- package/dist-types/mixpanel-analytics.d.ts +2 -0
- package/dist-types/one-dollar-stats.d.ts +2 -0
- package/dist-types/openai-pixel.d.ts +2 -0
- package/dist-types/pinterest-tag.d.ts +2 -0
- package/dist-types/pirsch.d.ts +2 -0
- package/dist-types/plausible-analytics.d.ts +2 -0
- package/dist-types/posthog.d.ts +2 -0
- package/dist-types/promptwatch.d.ts +2 -0
- package/dist-types/reddit-pixel.d.ts +2 -0
- package/dist-types/registry.d.ts +2 -485
- package/dist-types/resolve.d.ts +2 -9
- package/dist-types/rudderstack.d.ts +2 -0
- package/dist-types/rybbit-analytics.d.ts +2 -0
- package/dist-types/segment.d.ts +2 -0
- package/dist-types/snapchat-pixel.d.ts +2 -0
- package/dist-types/tiktok-pixel.d.ts +2 -0
- package/dist-types/types.d.ts +2 -314
- package/dist-types/umami-analytics.d.ts +2 -0
- package/dist-types/vendors/_shared/attributes.d.ts +2 -35
- package/dist-types/vendors/_shared/google-consent.d.ts +2 -47
- package/dist-types/vendors/_shared/install-builders.d.ts +2 -30
- package/dist-types/vendors/_shared/required-id.d.ts +2 -0
- package/dist-types/vendors/_shared/script-url.d.ts +2 -75
- package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +2 -92
- package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +2 -289
- package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +2 -105
- package/dist-types/vendors/ads-and-pixels/openai-pixel.d.ts +2 -211
- package/dist-types/vendors/ads-and-pixels/pinterest-tag.d.ts +2 -295
- package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +2 -210
- package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +2 -171
- package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +2 -106
- package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +2 -183
- package/dist-types/vendors/analytics/adobe-analytics.d.ts +2 -75
- package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +2 -62
- package/dist-types/vendors/analytics/amplitude.d.ts +2 -234
- package/dist-types/vendors/analytics/clearbit.d.ts +2 -60
- package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +2 -67
- package/dist-types/vendors/analytics/databuddy.d.ts +2 -147
- package/dist-types/vendors/analytics/fathom-analytics.d.ts +2 -90
- package/dist-types/vendors/analytics/google-tag.d.ts +2 -95
- package/dist-types/vendors/analytics/heap.d.ts +2 -316
- package/dist-types/vendors/analytics/hightouch.d.ts +2 -285
- package/dist-types/vendors/analytics/hotjar.d.ts +2 -73
- package/dist-types/vendors/analytics/logrocket.d.ts +2 -101
- package/dist-types/vendors/analytics/matomo-analytics.d.ts +2 -41
- package/dist-types/vendors/analytics/microsoft-clarity.d.ts +2 -97
- package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +2 -113
- package/dist-types/vendors/analytics/one-dollar-stats.d.ts +2 -39
- package/dist-types/vendors/analytics/pirsch.d.ts +2 -96
- package/dist-types/vendors/analytics/plausible-analytics.d.ts +2 -122
- package/dist-types/vendors/analytics/posthog.d.ts +2 -175
- package/dist-types/vendors/analytics/promptwatch.d.ts +2 -36
- package/dist-types/vendors/analytics/rudderstack.d.ts +2 -330
- package/dist-types/vendors/analytics/rybbit-analytics.d.ts +2 -82
- package/dist-types/vendors/analytics/segment.d.ts +2 -164
- package/dist-types/vendors/analytics/umami-analytics.d.ts +2 -93
- package/dist-types/vendors/analytics/vercel-analytics.d.ts +2 -66
- package/dist-types/vendors/email-and-sms/klaviyo.d.ts +2 -0
- package/dist-types/vendors/functional/crisp.d.ts +2 -78
- package/dist-types/vendors/functional/front-chat.d.ts +2 -62
- package/dist-types/vendors/functional/intercom.d.ts +2 -135
- package/dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts +2 -39
- package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +2 -96
- package/dist-types/vercel-analytics.d.ts +2 -0
- package/dist-types/x-pixel.d.ts +2 -0
- package/docs/README.md +129 -63
- package/docs/assets/v3/bottom-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/brand-preferences.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/headless-bar-html.png +0 -0
- package/docs/assets/v3/headless-bar-mobile.png +0 -0
- package/docs/assets/v3/headless-bar.png +0 -0
- package/docs/assets/v3/slim-bar.png +0 -0
- package/docs/concepts/choose-your-setup.md +87 -0
- package/docs/concepts/consent-categories.md +84 -0
- package/docs/{guides → concepts}/consent-state.md +89 -105
- package/docs/{guides → concepts}/data-fetching.md +31 -27
- package/docs/concepts/how-consent-works.md +123 -0
- package/docs/concepts/policies.md +71 -0
- package/docs/customization/class-names.md +202 -0
- package/docs/customization/dark-mode.md +157 -0
- package/docs/customization/motion.md +119 -0
- package/docs/customization/overview.md +67 -34
- package/docs/customization/recipes.md +839 -49
- package/docs/customization/slots.md +216 -35
- package/docs/customization/stylesheets.md +147 -0
- package/docs/customization/tailwind.md +842 -0
- package/docs/customization/tokens.md +163 -96
- package/docs/customization/translations.md +60 -3
- package/docs/frameworks/astro/embeds.md +160 -0
- package/docs/frameworks/astro/network-blocker.md +86 -0
- package/docs/frameworks/astro/scripts.md +155 -0
- package/docs/frameworks/html/embeds.md +142 -0
- package/docs/frameworks/html/network-blocker.md +105 -0
- package/docs/frameworks/html/scripts.md +164 -0
- package/docs/frameworks/javascript/scripts.md +137 -0
- package/docs/frameworks/next/embeds.md +90 -0
- package/docs/frameworks/next/network-blocker.md +153 -0
- package/docs/frameworks/next/scripts.md +196 -0
- package/docs/frameworks/nuxt/embeds.md +81 -0
- package/docs/frameworks/nuxt/network-blocker.md +97 -0
- package/docs/frameworks/nuxt/scripts.md +89 -0
- package/docs/frameworks/react/embeds.md +89 -0
- package/docs/frameworks/react/network-blocker.md +140 -0
- package/docs/frameworks/react/scripts.md +115 -0
- package/docs/frameworks/svelte/embeds.md +96 -0
- package/docs/frameworks/svelte/network-blocker.md +141 -0
- package/docs/frameworks/svelte/scripts.md +144 -0
- package/docs/frameworks/sveltekit/embeds.md +103 -0
- package/docs/frameworks/sveltekit/network-blocker.md +159 -0
- package/docs/frameworks/sveltekit/scripts.md +172 -0
- package/docs/frameworks/tanstack-start/embeds.md +96 -0
- package/docs/frameworks/tanstack-start/network-blocker.md +145 -0
- package/docs/frameworks/tanstack-start/scripts.md +103 -0
- package/docs/frameworks/vue/embeds.md +84 -0
- package/docs/frameworks/vue/network-blocker.md +99 -0
- package/docs/frameworks/vue/scripts.md +93 -0
- package/docs/guides/banner-experiments.md +654 -0
- package/docs/guides/troubleshooting.md +120 -47
- package/docs/guides/verify-consent.md +81 -49
- package/docs/integrations/adobe-analytics.md +167 -159
- package/docs/integrations/ahrefs-analytics.md +152 -154
- package/docs/integrations/amplitude.md +162 -156
- package/docs/integrations/building-integrations.md +136 -37
- package/docs/integrations/clearbit.md +154 -154
- package/docs/integrations/cloudflare-web-analytics.md +156 -156
- package/docs/integrations/cloudflare-zaraz.md +209 -261
- package/docs/integrations/crisp.md +164 -158
- package/docs/integrations/databuddy.md +157 -173
- package/docs/integrations/fathom-analytics.md +158 -156
- package/docs/integrations/front-chat.md +167 -167
- package/docs/integrations/google-maps.md +118 -83
- package/docs/integrations/google-tag-manager.md +178 -163
- package/docs/integrations/google-tag.md +163 -160
- package/docs/integrations/heap.md +163 -155
- package/docs/integrations/hightouch.md +161 -157
- package/docs/integrations/hotjar.md +159 -155
- package/docs/integrations/intercom.md +183 -153
- package/docs/integrations/klaviyo.md +486 -0
- package/docs/integrations/linkedin-insights.md +174 -150
- package/docs/integrations/logrocket.md +160 -156
- package/docs/integrations/matomo-analytics.md +188 -178
- package/docs/integrations/meta-pixel.md +188 -150
- package/docs/integrations/microsoft-clarity.md +163 -155
- package/docs/integrations/microsoft-uet.md +148 -154
- package/docs/integrations/mixpanel-analytics.md +155 -160
- package/docs/integrations/one-dollar-stats.md +166 -165
- package/docs/integrations/openai-pixel.md +204 -301
- package/docs/integrations/overview.md +137 -80
- package/docs/integrations/pinterest-tag.md +191 -183
- package/docs/integrations/pirsch.md +169 -159
- package/docs/integrations/plausible-analytics.md +172 -158
- package/docs/integrations/posthog.md +313 -240
- package/docs/integrations/promptwatch.md +154 -154
- package/docs/integrations/reddit-pixel.md +185 -157
- package/docs/integrations/rudderstack.md +201 -186
- package/docs/integrations/rybbit-analytics.md +171 -160
- package/docs/integrations/segment.md +182 -154
- package/docs/integrations/snapchat-pixel.md +186 -156
- package/docs/integrations/tiktok-pixel.md +171 -150
- package/docs/integrations/umami-analytics.md +163 -158
- package/docs/integrations/vercel-analytics.md +166 -157
- package/docs/integrations/x-pixel.md +176 -150
- package/docs/integrations/youtube.md +121 -86
- package/docs/upgrade-v3.md +496 -467
- package/package.json +10 -257
- package/dist-types/__tests__/helpers.d.ts +0 -141
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/frameworks/javascript/script-loader.md +0 -100
- package/docs/frameworks/next/script-loader.md +0 -216
- package/docs/frameworks/react/script-loader.md +0 -69
- package/docs/guides/deployment-modes.md +0 -75
- package/docs/guides/shared-consent-controls.md +0 -158
- package/docs/integrations/clear-on-revocation.md +0 -167
- package/docs/integrations/granular-consent.md +0 -210
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scripts
|
|
3
|
+
description: Register vendor scripts with @c15t/browser, createConsentRuntime or
|
|
4
|
+
a consent kernel in JavaScript, and control what happens when a visitor
|
|
5
|
+
withdraws permission.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Register scripts where you start c15t
|
|
10
|
+
|
|
11
|
+
Pass the `scripts` list to whichever API starts c15t. The list is the same in
|
|
12
|
+
each case, built from `@c15t/integrations` helpers or your own script objects:
|
|
13
|
+
|
|
14
|
+
| Setup | Where `scripts` goes |
|
|
15
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| `@c15t/browser` | `init({ backendURL, scripts })`, as in the [quickstart](https://c15t.com/docs/frameworks/javascript/quickstart#start-c15t). |
|
|
17
|
+
| `c15t/runtime` | `createConsentRuntime({ mode, scripts })`, as in [headless](https://c15t.com/docs/frameworks/javascript/headless#create-the-runtime). |
|
|
18
|
+
| Your own kernel | `createScriptLoader({ kernel, scripts })`. See [script loader](https://c15t.com/docs/frameworks/javascript/modules/script-loader#attach-the-loader-to-your-own-kernel). |
|
|
19
|
+
|
|
20
|
+
The quickstart's `src/scripts.ts` shows PostHog and X Pixel:
|
|
21
|
+
|
|
22
|
+
```ts title="src/scripts.ts"
|
|
23
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
24
|
+
import { xPixel } from '@c15t/integrations/x-pixel';
|
|
25
|
+
|
|
26
|
+
export const scripts = [
|
|
27
|
+
posthog({
|
|
28
|
+
id: 'phc_your_project_key',
|
|
29
|
+
initOptions: { cookieless_mode: 'never' },
|
|
30
|
+
loadMode: 'after-consent',
|
|
31
|
+
}),
|
|
32
|
+
xPixel({ pixelId: 'your-pixel-id' }),
|
|
33
|
+
];
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Each [integration guide](../../integrations/overview.md) gives the helper and
|
|
37
|
+
options for one vendor. For a vendor without a helper, write the object
|
|
38
|
+
yourself:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const scripts = [
|
|
42
|
+
{
|
|
43
|
+
id: 'analytics',
|
|
44
|
+
src: 'https://analytics.example/sdk.js',
|
|
45
|
+
category: 'measurement',
|
|
46
|
+
onLoad: () => window.analytics?.track('page_view'),
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`window.analytics` stands for your vendor's global. The
|
|
52
|
+
[script loader](https://c15t.com/docs/frameworks/javascript/modules/script-loader) page lists
|
|
53
|
+
every field and callback.
|
|
54
|
+
|
|
55
|
+
## When the script loader downloads
|
|
56
|
+
|
|
57
|
+
With `@c15t/browser` from npm, the script loader and the network blocker
|
|
58
|
+
share a separate chunk. It loads when c15t starts, and only if `scripts` is
|
|
59
|
+
not empty or `networkBlocker` has rules, so a page with neither never
|
|
60
|
+
downloads it. On a page with either, the browser requests it after your
|
|
61
|
+
JavaScript has run, which delays a returning visitor's consented scripts and
|
|
62
|
+
held requests by one request.
|
|
63
|
+
|
|
64
|
+
Your bundler names that chunk, so c15t cannot link it from your HTML. To
|
|
65
|
+
start the download earlier, add a `<link rel="modulepreload">` for the chunk
|
|
66
|
+
your build emits for `@c15t/core/dist/modules/loader-and-blocker.js`. With
|
|
67
|
+
Vite, `.vite/manifest.json` lists it under that path when `build.manifest` is
|
|
68
|
+
on. Give the link `fetchpriority="low"`. c15t needs the chunk only when it
|
|
69
|
+
starts, and at the default priority the preload can delay your app's own
|
|
70
|
+
chunks over HTTP/1.1. The script-tag build, `c15t.js`, already contains the
|
|
71
|
+
loader.
|
|
72
|
+
|
|
73
|
+
## Gate a snippet in your HTML
|
|
74
|
+
|
|
75
|
+
`@c15t/browser` also runs `<script type="text/plain" data-c15t-category>`
|
|
76
|
+
tags in your `index.html` once their category is allowed, in page order. Use
|
|
77
|
+
it for a vendor snippet you would rather keep in HTML. `createConsentRuntime`
|
|
78
|
+
and a plain kernel do not scan the page for these tags;
|
|
79
|
+
`activateGatedScripts(snapshot)` from `@c15t/browser` runs them for you.
|
|
80
|
+
|
|
81
|
+
With the `nonce` option set, `@c15t/browser` runs only the tags that carry the
|
|
82
|
+
same `nonce`, and marks the others `data-c15t-activated="untrusted"`. Pass
|
|
83
|
+
`{ nonce }` as the third argument of `activateGatedScripts` for the same
|
|
84
|
+
check. See [Content Security Policy](https://c15t.com/docs/frameworks/javascript/content-security-policy).
|
|
85
|
+
|
|
86
|
+
## When a visitor withdraws permission
|
|
87
|
+
|
|
88
|
+
A script that has run cannot be unloaded. `@c15t/browser` and
|
|
89
|
+
`createConsentRuntime` reload the page after a visitor turns off a category
|
|
90
|
+
they had allowed, so the new page starts without that vendor. Allowing a
|
|
91
|
+
category never reloads.
|
|
92
|
+
|
|
93
|
+
Set `reloadOnConsentRevoked: false` to handle withdrawal yourself, for
|
|
94
|
+
example with the vendor's opt-out call in the script's `onConsentChange`.
|
|
95
|
+
Until the next page load, code that already ran keeps running.
|
|
96
|
+
`callbacks.onBeforeConsentRevocationReload` runs right before the reload, for
|
|
97
|
+
work that must finish first. A kernel you create yourself does not reload;
|
|
98
|
+
add `watchRevocationReload` from `c15t` if you want it.
|
|
99
|
+
|
|
100
|
+
## Keep one owner per vendor
|
|
101
|
+
|
|
102
|
+
Use stable script IDs and remove the vendor's original snippet, so each
|
|
103
|
+
vendor loads once and only through c15t. Ordinary scripts wait for
|
|
104
|
+
permission. Helpers with `alwaysLoad` load at once and pass consent to the
|
|
105
|
+
vendor's own API instead. Read the individual integration guide before
|
|
106
|
+
assuming all helpers have the same network behavior.
|
|
107
|
+
|
|
108
|
+
Create `init()` or the runtime once per page. A second client, or a second
|
|
109
|
+
loader on a kernel that `@c15t/browser` or `createConsentRuntime` owns, loads
|
|
110
|
+
vendors twice.
|
|
111
|
+
|
|
112
|
+
## Let visitors turn off one vendor
|
|
113
|
+
|
|
114
|
+
A visitor can allow marketing and still switch off one vendor in it. Declare
|
|
115
|
+
the vendors in the `vendors` option and render the switch yourself; helpers
|
|
116
|
+
from `@c15t/integrations` already carry their vendor slug. See
|
|
117
|
+
[vendor consent](https://c15t.com/docs/frameworks/javascript/vendor-consent).
|
|
118
|
+
|
|
119
|
+
## Clear stored tracking data
|
|
120
|
+
|
|
121
|
+
Script gating does not remove cookies or Web Storage entries that a script
|
|
122
|
+
already wrote. Configure [clear on revocation](https://c15t.com/docs/frameworks/javascript/clear-on-revocation)
|
|
123
|
+
to delete them when their category is denied.
|
|
124
|
+
|
|
125
|
+
## Send your own events to allowed vendors
|
|
126
|
+
|
|
127
|
+
To send your own analytics events only to the vendors a visitor allows, use
|
|
128
|
+
the event dispatcher from `@c15t/integrations`. See
|
|
129
|
+
[send events only to allowed integrations](../../integrations/overview.md#send-events-only-to-allowed-integrations).
|
|
130
|
+
|
|
131
|
+
## Check it works
|
|
132
|
+
|
|
133
|
+
Run the app in a private window with the Network tab open.
|
|
134
|
+
|
|
135
|
+
1. Filter for each vendor's host. Nothing loads before a choice.
|
|
136
|
+
2. Allow one category. Only that category's vendors load.
|
|
137
|
+
3. Withdraw it. The page reloads and the vendor stays absent.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Embeds
|
|
3
|
+
description: Keep YouTube videos, maps and other iframes out of a Next.js page
|
|
4
|
+
until their consent category is allowed, with ConsentGate or the iframe
|
|
5
|
+
blocker in ConsentRoot.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Pick a method
|
|
10
|
+
|
|
11
|
+
| Method | Use it when |
|
|
12
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `ConsentGate` | You render the iframe in a React component and want a placeholder until the visitor allows its category. |
|
|
14
|
+
| The iframe blocker | The iframe comes from markup you do not render with a component, such as CMS or Markdown content. |
|
|
15
|
+
|
|
16
|
+
Both keep the iframe's `src` out of the page until the category is allowed, so
|
|
17
|
+
the embed's host receives no request before consent.
|
|
18
|
+
|
|
19
|
+
## Gate an embed with ConsentGate
|
|
20
|
+
|
|
21
|
+
Wrap the iframe in `ConsentGate` in a Client Component:
|
|
22
|
+
|
|
23
|
+
```tsx title="components/video-embed.tsx"
|
|
24
|
+
'use client';
|
|
25
|
+
|
|
26
|
+
import { ConsentGate } from 'c15t/next';
|
|
27
|
+
|
|
28
|
+
export const VideoEmbed = () => (
|
|
29
|
+
<ConsentGate category="measurement">
|
|
30
|
+
<iframe
|
|
31
|
+
className="video-frame"
|
|
32
|
+
sandbox="allow-scripts allow-same-origin allow-presentation"
|
|
33
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y"
|
|
34
|
+
title="YouTube video"
|
|
35
|
+
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
|
36
|
+
allowFullScreen
|
|
37
|
+
/>
|
|
38
|
+
</ConsentGate>
|
|
39
|
+
);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Import `VideoEmbed` into any page or Server Component under the layout that
|
|
43
|
+
mounts `ConsentRoot`; only this file needs `'use client'`. The iframe stays out
|
|
44
|
+
of the page until the visitor allows measurement, and a placeholder with a
|
|
45
|
+
button that opens the preference dialog takes its place.
|
|
46
|
+
|
|
47
|
+
Pick the category that matches what the embed does. The example uses
|
|
48
|
+
measurement for YouTube because its player measures views. A map or chat
|
|
49
|
+
widget usually belongs under functionality or experience.
|
|
50
|
+
|
|
51
|
+
What the server HTML contains depends on how your layout passes consent state.
|
|
52
|
+
[ConsentGate](https://c15t.com/docs/frameworks/next/components/consent-gate) covers the
|
|
53
|
+
default, awaited and Pages Router layouts, the placeholder and its props.
|
|
54
|
+
|
|
55
|
+
## Gate an iframe with the iframe blocker
|
|
56
|
+
|
|
57
|
+
`ConsentRoot` runs the iframe blocker in the browser by default. Give an
|
|
58
|
+
iframe `data-src` instead of `src`, and a `data-category`:
|
|
59
|
+
|
|
60
|
+
```html title="Markup from your CMS"
|
|
61
|
+
<iframe
|
|
62
|
+
data-src="https://www.google.com/maps/embed?pb=YOUR_EMBED_ID"
|
|
63
|
+
data-category="functionality"
|
|
64
|
+
title="Store map"
|
|
65
|
+
></iframe>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The server HTML has no `src`, so nothing loads before hydration. After
|
|
69
|
+
hydration, c15t sets `src` from `data-src` when the category is allowed and
|
|
70
|
+
removes `src` when the category is withdrawn. It watches the page, so iframes
|
|
71
|
+
added by client-side navigation are handled too. Add `data-vendor` with a
|
|
72
|
+
vendor ID to also block the iframe while the visitor has turned that vendor
|
|
73
|
+
off.
|
|
74
|
+
|
|
75
|
+
To turn the blocker off, pass `options={{ iframeBlocker: false }}` to
|
|
76
|
+
`ConsentRoot`.
|
|
77
|
+
|
|
78
|
+
## Verify the embeds
|
|
79
|
+
|
|
80
|
+
Open the production build in a private window with DevTools open, under a
|
|
81
|
+
policy that asks for consent.
|
|
82
|
+
|
|
83
|
+
1. View the page source. No iframe has a `src` pointing at
|
|
84
|
+
`youtube-nocookie.com` or `google.com/maps`, and the Network panel shows no
|
|
85
|
+
request to them.
|
|
86
|
+
2. Allow the embed's category and save. The iframe loads.
|
|
87
|
+
3. Withdraw the category and save. The page reloads without the embed.
|
|
88
|
+
|
|
89
|
+
The [YouTube](../../integrations/youtube.md) and
|
|
90
|
+
[Google Maps](../../integrations/google-maps.md) guides cover sizing and titles.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Network blocker
|
|
3
|
+
description: Hold fetch and XMLHttpRequest calls in a Next.js app until their
|
|
4
|
+
consent category is allowed, with networkBlocker rules on ConsentRoot.
|
|
5
|
+
group: frameworks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Add rules
|
|
9
|
+
|
|
10
|
+
The network blocker stops `fetch` and `XMLHttpRequest` calls to domains you
|
|
11
|
+
list until the visitor grants their category. Use it for beacons and API calls
|
|
12
|
+
that bypass script loading, such as a pixel fired by an SDK already on the
|
|
13
|
+
page. Load vendor SDKs through [`scripts`](./scripts.md)
|
|
14
|
+
first, so they do not run at all before consent.
|
|
15
|
+
|
|
16
|
+
Define the rules in `components/consent.tsx` and pass them to `ConsentRoot`.
|
|
17
|
+
`onRequestBlocked` is a function, so the rules cannot come from a Server
|
|
18
|
+
Component.
|
|
19
|
+
|
|
20
|
+
```tsx title="components/consent.tsx (partial)"
|
|
21
|
+
import type { ConsentRootProps } from 'c15t/next';
|
|
22
|
+
|
|
23
|
+
const networkBlocker: NonNullable<ConsentRootProps['networkBlocker']> = {
|
|
24
|
+
rules: [
|
|
25
|
+
{
|
|
26
|
+
id: 'google-analytics',
|
|
27
|
+
domain: 'google-analytics.com',
|
|
28
|
+
category: 'measurement',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'meta-pixel',
|
|
32
|
+
domain: 'facebook.com',
|
|
33
|
+
pathIncludes: '/tr',
|
|
34
|
+
category: 'marketing',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// On the existing root:
|
|
40
|
+
<ConsentRoot
|
|
41
|
+
state={state}
|
|
42
|
+
config={consentConfig}
|
|
43
|
+
scripts={scripts}
|
|
44
|
+
networkBlocker={networkBlocker}
|
|
45
|
+
>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Keep the page content inside `ConsentRoot`. Blocking starts when it renders, so
|
|
49
|
+
components outside it that send requests while rendering, and modules evaluated
|
|
50
|
+
before it, are not covered. Requests your server makes, in Server Components,
|
|
51
|
+
route handlers or `getServerSideProps`, are not blocked either.
|
|
52
|
+
|
|
53
|
+
## Match requests with rules
|
|
54
|
+
|
|
55
|
+
Each rule names a `domain` and the consent `category` a request needs. The
|
|
56
|
+
domain also matches its subdomains: `google-analytics.com` covers
|
|
57
|
+
`www.google-analytics.com`. `pathIncludes` narrows the rule to paths that
|
|
58
|
+
contain a substring, and `methods` narrows it to HTTP methods. A request is
|
|
59
|
+
blocked when a matching rule's condition is not met by the visitor's
|
|
60
|
+
effective permissions.
|
|
61
|
+
|
|
62
|
+
`category` takes the same conditions as scripts:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
{ category: 'measurement' }
|
|
66
|
+
{ category: { and: ['measurement', 'marketing'] } }
|
|
67
|
+
{ category: { or: ['measurement', 'marketing'] } }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Add `vendor` to also block the request while the visitor has turned that
|
|
71
|
+
vendor off. IAB TCF rules use `vendorId` and the `iab*` purpose fields
|
|
72
|
+
instead.
|
|
73
|
+
|
|
74
|
+
| Option | Default | Purpose |
|
|
75
|
+
| -------------------- | -------- | ------------------------------------------------------------ |
|
|
76
|
+
| `rules` | required | Rules described above |
|
|
77
|
+
| `enabled` | `true` | Set `false` to keep the rules but stop blocking |
|
|
78
|
+
| `logBlockedRequests` | `true` | Log each blocked request with `console.warn` |
|
|
79
|
+
| `onRequestBlocked` | none | Called with `{ method, url, rule }` for each blocked request |
|
|
80
|
+
|
|
81
|
+
## What a blocked request looks like
|
|
82
|
+
|
|
83
|
+
The blocker wraps `window.fetch` and `XMLHttpRequest`. A blocked `fetch`
|
|
84
|
+
resolves to a `451` response with the status text
|
|
85
|
+
`Request blocked by consent`, and nothing is sent. A blocked XHR is aborted
|
|
86
|
+
and fires an `error` event. Requests that match no rule are not delayed.
|
|
87
|
+
|
|
88
|
+
## When blocking starts
|
|
89
|
+
|
|
90
|
+
The provider holds matching requests from its first render in the browser,
|
|
91
|
+
before any of its children render or run effects. That covers requests from
|
|
92
|
+
child components, including their mount effects, and from effects in
|
|
93
|
+
components rendered next to the provider. The blocker module itself loads
|
|
94
|
+
after mount and decides each held request. Apps without `networkBlocker` do
|
|
95
|
+
not download it.
|
|
96
|
+
|
|
97
|
+
The standalone `useNetworkBlocker` hook works the same way from the first
|
|
98
|
+
render of the component that calls it. That render patches `fetch` and
|
|
99
|
+
`XMLHttpRequest`. If React throws the render away and never commits it, the
|
|
100
|
+
hold ends after 10 seconds. Nothing checked consent for the requests it held,
|
|
101
|
+
so they fail the way the blocker fails a blocked request: a 451 response for
|
|
102
|
+
`fetch`, a failed XHR. The same happens when the component unmounts before
|
|
103
|
+
the blocker loads.
|
|
104
|
+
|
|
105
|
+
While consent is unknown, a matching request that would be blocked waits
|
|
106
|
+
instead of failing. Consent is unknown until the policy has loaded, which is
|
|
107
|
+
also when a returning visitor's stored choice takes effect. The request is
|
|
108
|
+
then sent if the choice allows it and blocked otherwise. If the policy fails
|
|
109
|
+
to load, optional categories stay denied and waiting requests are blocked.
|
|
110
|
+
If the policy request never finishes, they keep waiting and are never sent.
|
|
111
|
+
|
|
112
|
+
A synchronous XHR cannot wait. Before the blocker module has loaded, a
|
|
113
|
+
matching one throws a `NetworkError` from `send()`. After that, one that
|
|
114
|
+
consent does not allow yet is blocked.
|
|
115
|
+
|
|
116
|
+
## What the network blocker cannot stop
|
|
117
|
+
|
|
118
|
+
The blocker only sees requests made after the provider starts rendering in
|
|
119
|
+
the browser, through `fetch` or `XMLHttpRequest`. It cannot stop:
|
|
120
|
+
|
|
121
|
+
* Code that runs before the provider renders: inline scripts in the HTML,
|
|
122
|
+
third-party tags in `<head>`, scripts loaded before hydration (such as
|
|
123
|
+
`next/script` with `beforeInteractive`), and client modules that evaluate
|
|
124
|
+
earlier. Webpack builds evaluate a route's client component modules when
|
|
125
|
+
its chunk loads, so their top-level code runs first. Turbopack evaluates a
|
|
126
|
+
client component module when its first element renders, which inside the
|
|
127
|
+
provider is after blocking starts.
|
|
128
|
+
* Code that saved its own reference to `fetch` or `XMLHttpRequest` before the
|
|
129
|
+
provider rendered.
|
|
130
|
+
* `navigator.sendBeacon`, `WebSocket`, `EventSource`, and requests made by
|
|
131
|
+
`<img>`, `<script>` or `<iframe>` elements, web workers and service
|
|
132
|
+
workers.
|
|
133
|
+
* With the standalone `useNetworkBlocker` hook instead of the provider
|
|
134
|
+
option, requests sent before the component that calls it renders.
|
|
135
|
+
Blocking starts in that component's first render, not the provider's.
|
|
136
|
+
|
|
137
|
+
Keep tracking calls out of that window:
|
|
138
|
+
|
|
139
|
+
* Send them from an effect or an event handler, never at module top level.
|
|
140
|
+
* Load vendor SDKs through `scripts` instead of a `<script>` tag or
|
|
141
|
+
`next/script`, so they wait for consent before they run at all.
|
|
142
|
+
* Check `useConsent('measurement')` (or the category you need) before you
|
|
143
|
+
call a vendor from your own code, and treat the blocker as a backstop.
|
|
144
|
+
|
|
145
|
+
## Verify the blocked requests
|
|
146
|
+
|
|
147
|
+
Open the production build in a private window with the DevTools Network panel
|
|
148
|
+
open, under a policy that asks for consent.
|
|
149
|
+
|
|
150
|
+
1. Before a choice, requests matching a rule are absent from the Network panel,
|
|
151
|
+
and the console logs each blocked request.
|
|
152
|
+
2. Allow the rule's category and save. Matching requests go out.
|
|
153
|
+
3. Reject, reload, and check that they stay absent.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scripts
|
|
3
|
+
description: Register vendor scripts in a Next.js ConsentRoot, check how each
|
|
4
|
+
vendor loads, let visitors turn off one vendor and clear stored data after
|
|
5
|
+
revocation.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install the script helpers
|
|
10
|
+
|
|
11
|
+
The [App Router](https://c15t.com/docs/frameworks/next/app-router),
|
|
12
|
+
[Pages Router](https://c15t.com/docs/frameworks/next/pages-router) and
|
|
13
|
+
[static export](https://c15t.com/docs/frameworks/next/static-export) guides already register
|
|
14
|
+
scripts. Use this page to add vendors to an existing setup or to change how
|
|
15
|
+
they load. Install the helpers if you have not:
|
|
16
|
+
|
|
17
|
+
| Package manager | Command |
|
|
18
|
+
| :-------------- | :------------------------------------- |
|
|
19
|
+
| npm | `npm install @c15t/integrations@alpha` |
|
|
20
|
+
| pnpm | `pnpm add @c15t/integrations@alpha` |
|
|
21
|
+
| yarn | `yarn add @c15t/integrations@alpha` |
|
|
22
|
+
| bun | `bun add @c15t/integrations@alpha` |
|
|
23
|
+
|
|
24
|
+
Keep `c15t.config.ts`, the manifest route and the layout from your router
|
|
25
|
+
guide. Adding a vendor needs no server change. Server-rendered state, a
|
|
26
|
+
streamed promise and browser initialization with `state={{}}` all reach the
|
|
27
|
+
same `ConsentRoot`, and no script loads until the browser has a resolved
|
|
28
|
+
policy and the visitor's choice allows it.
|
|
29
|
+
|
|
30
|
+
## Register scripts in a client wrapper
|
|
31
|
+
|
|
32
|
+
The [runnable Next.js example](https://c15t.com/docs/examples) uses PostHog for measurement and
|
|
33
|
+
X Pixel for marketing. Replace the placeholder IDs `phc_your_project_key` and
|
|
34
|
+
`your-pixel-id` with your own. Remove a vendor's entry to leave it out, or
|
|
35
|
+
replace its helper with the [integration](../../integrations/overview.md) your
|
|
36
|
+
application uses. Include the
|
|
37
|
+
measurement and marketing categories in your policy for these two vendors.
|
|
38
|
+
|
|
39
|
+
Create `lib/scripts.ts` with the example's script configuration:
|
|
40
|
+
|
|
41
|
+
```ts title="lib/scripts.ts"
|
|
42
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
43
|
+
import { xPixel } from '@c15t/integrations/x-pixel';
|
|
44
|
+
import type { Script } from 'c15t';
|
|
45
|
+
|
|
46
|
+
export const scripts: Script[] = [
|
|
47
|
+
posthog({
|
|
48
|
+
id: 'phc_your_project_key',
|
|
49
|
+
initOptions: { cookieless_mode: 'never' },
|
|
50
|
+
loadMode: 'after-consent',
|
|
51
|
+
}),
|
|
52
|
+
xPixel({ pixelId: 'your-pixel-id' }),
|
|
53
|
+
];
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
PostHog waits for measurement consent here. `cookieless_mode: 'never'` disables
|
|
57
|
+
cookieless capture after rejection. X Pixel waits for marketing consent. Remove
|
|
58
|
+
any existing loader for these vendors, including `next/script` and tag-manager
|
|
59
|
+
entries, so each integration loads once.
|
|
60
|
+
|
|
61
|
+
Create this client wrapper. It owns everything that has to run in the browser:
|
|
62
|
+
the consent config, the scripts, the banner, the dialog and a persistent
|
|
63
|
+
preferences link. The layout or `_app.tsx` passes only the visitor's state.
|
|
64
|
+
|
|
65
|
+
```tsx title="components/consent.tsx"
|
|
66
|
+
'use client';
|
|
67
|
+
|
|
68
|
+
import {
|
|
69
|
+
ConsentBanner,
|
|
70
|
+
ConsentDialog,
|
|
71
|
+
ConsentDialogLink,
|
|
72
|
+
ConsentRoot,
|
|
73
|
+
} from 'c15t/next';
|
|
74
|
+
import type { ConsentRootProps } from 'c15t/next';
|
|
75
|
+
import type { ReactNode } from 'react';
|
|
76
|
+
|
|
77
|
+
import { consentConfig } from '@/c15t.config';
|
|
78
|
+
import { scripts } from '@/lib/scripts';
|
|
79
|
+
|
|
80
|
+
interface ConsentProps {
|
|
81
|
+
children: ReactNode;
|
|
82
|
+
state: ConsentRootProps['state'];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const Consent = ({ children, state }: ConsentProps) => (
|
|
86
|
+
<ConsentRoot state={state} config={consentConfig} scripts={scripts}>
|
|
87
|
+
{children}
|
|
88
|
+
<ConsentBanner />
|
|
89
|
+
<ConsentDialog />
|
|
90
|
+
<footer>
|
|
91
|
+
<ConsentDialogLink>Privacy settings</ConsentDialogLink>
|
|
92
|
+
</footer>
|
|
93
|
+
</ConsentRoot>
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Import `consentConfig` in this `'use client'` file, as shown, and do not pass it
|
|
98
|
+
from a Server Component. `defineConsentConfig` marks its result with a symbol
|
|
99
|
+
key, and React cannot send an object with symbol keys from a Server Component
|
|
100
|
+
to a Client Component.
|
|
101
|
+
|
|
102
|
+
`ConsentRoot` already provides the consent runtime. Mount this wrapper once and
|
|
103
|
+
do not add a second provider. Keep your site's content and footer inside it.
|
|
104
|
+
|
|
105
|
+
The files import through the `@/` path alias that `create-next-app` sets up.
|
|
106
|
+
With a `src/` directory the alias points at `src/`, so the same imports work.
|
|
107
|
+
Without the alias, use relative paths.
|
|
108
|
+
|
|
109
|
+
## Register several vendors
|
|
110
|
+
|
|
111
|
+
Every helper goes in the same `scripts` array in `lib/scripts.ts`, in the App
|
|
112
|
+
Router and the Pages Router alike. This array loads Google Tag Manager and
|
|
113
|
+
Meta Pixel together:
|
|
114
|
+
|
|
115
|
+
```ts title="lib/scripts.ts (partial)"
|
|
116
|
+
import { googleTagManager } from '@c15t/integrations/google-tag-manager';
|
|
117
|
+
import { metaPixel } from '@c15t/integrations/meta-pixel';
|
|
118
|
+
import type { Script } from 'c15t';
|
|
119
|
+
|
|
120
|
+
export const scripts: Script[] = [
|
|
121
|
+
googleTagManager({ id: 'GTM-XXXXXXX' }),
|
|
122
|
+
metaPixel({ pixelId: 'YOUR_PIXEL_ID' }),
|
|
123
|
+
];
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Each helper keeps its own consent rules. Meta Pixel waits for marketing, and
|
|
127
|
+
Google Tag Manager follows the
|
|
128
|
+
[Consent Mode contract](../../integrations/google-tag-manager.md). Remove the
|
|
129
|
+
container and pixel snippets from `pages/_document.tsx` or your layout, so each
|
|
130
|
+
vendor loads once. Both helpers throw on an empty ID, so leave a helper out of
|
|
131
|
+
the array until you have its ID.
|
|
132
|
+
|
|
133
|
+
## Check each vendor's loading behavior
|
|
134
|
+
|
|
135
|
+
The example configures PostHog to load after consent and turns off cookieless
|
|
136
|
+
capture. Its default helper can load before consent and use the SDK's own
|
|
137
|
+
consent controls. Read the [PostHog guide](../../integrations/posthog.md) before
|
|
138
|
+
you change those settings.
|
|
139
|
+
|
|
140
|
+
Ordinary scripts wait for their category's permission. Give each script a
|
|
141
|
+
stable, unique `id`, and remove any other loader for the same vendor. Helpers
|
|
142
|
+
with `alwaysLoad` can load an SDK before permission is granted, so a category
|
|
143
|
+
alone does not guarantee that no request happens. See the
|
|
144
|
+
[vendor guides](../../integrations/overview.md) for each helper's contract.
|
|
145
|
+
|
|
146
|
+
Removing a script element cannot undo JavaScript that already ran or requests
|
|
147
|
+
already sent. When a visitor turns off a category they had granted,
|
|
148
|
+
`ConsentRoot` reloads the page, so the next page runs only permitted code. See
|
|
149
|
+
[reload after revocation](https://c15t.com/docs/frameworks/next/components/consent-root#reload-after-revocation).
|
|
150
|
+
|
|
151
|
+
Use [custom integrations](../../integrations/building-integrations.md) for a
|
|
152
|
+
vendor without a helper. Google helpers follow the
|
|
153
|
+
[Consent Mode contract](../../integrations/google-tag-manager.md).
|
|
154
|
+
|
|
155
|
+
## Embeds and other requests
|
|
156
|
+
|
|
157
|
+
Scripts cover vendor code c15t loads for you. For the rest:
|
|
158
|
+
|
|
159
|
+
* [Embeds](./embeds.md) keeps iframes out of the page with
|
|
160
|
+
`ConsentGate` or the iframe blocker.
|
|
161
|
+
* [Network blocker](./network-blocker.md) holds `fetch` and
|
|
162
|
+
XHR calls that match a rule until their category is allowed.
|
|
163
|
+
|
|
164
|
+
## Let visitors turn off one vendor
|
|
165
|
+
|
|
166
|
+
A visitor can allow marketing and still switch off one vendor in it. Declare
|
|
167
|
+
the vendors and pass them to `ConsentRoot` as `vendors`; helpers from
|
|
168
|
+
`@c15t/integrations` already carry their vendor slug. See
|
|
169
|
+
[vendor consent](https://c15t.com/docs/frameworks/next/vendor-consent).
|
|
170
|
+
|
|
171
|
+
## Clear stored tracking data
|
|
172
|
+
|
|
173
|
+
Script gating does not remove cookies or Web Storage entries a script already
|
|
174
|
+
wrote. Pass `clearOnRevocation` to `ConsentRoot` to remove declared data when
|
|
175
|
+
its category is denied. See
|
|
176
|
+
[clear on revocation](https://c15t.com/docs/frameworks/next/clear-on-revocation) for
|
|
177
|
+
configuration and browser limits.
|
|
178
|
+
|
|
179
|
+
To send your own events only to allowed integrations, see
|
|
180
|
+
[send events only to allowed integrations](../../integrations/overview.md#send-events-only-to-allowed-integrations).
|
|
181
|
+
|
|
182
|
+
## Verify the integration
|
|
183
|
+
|
|
184
|
+
Open the production build in a fresh browser session with DevTools open.
|
|
185
|
+
|
|
186
|
+
1. Under an opt-in policy, neither PostHog nor X Pixel requests anything before
|
|
187
|
+
you choose.
|
|
188
|
+
2. Open **Privacy settings** and turn on **Analytics** (the `measurement` category) only. PostHog loads and
|
|
189
|
+
X Pixel stays blocked.
|
|
190
|
+
3. Reject, reload, and reopen **Privacy settings**. The rejection is still
|
|
191
|
+
selected and neither vendor loads.
|
|
192
|
+
4. Turn a granted category off. The page reloads and that vendor does not load
|
|
193
|
+
again.
|
|
194
|
+
|
|
195
|
+
The [runnable example](https://c15t.com/docs/examples) has the same script definitions, and
|
|
196
|
+
[the consent checks](../../guides/verify-consent.md) cover the release checklist.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Embeds
|
|
3
|
+
description: Keep YouTube videos, maps and other iframes out of a Nuxt page
|
|
4
|
+
until their consent category is allowed, with ConsentGate or the iframe
|
|
5
|
+
blocker.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Pick a method
|
|
10
|
+
|
|
11
|
+
| Method | Use it when |
|
|
12
|
+
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `ConsentGate` | You render the iframe in a Vue component and want a placeholder with a way to open preferences. The server HTML respects the visitor's choice. |
|
|
14
|
+
| The iframe blocker | The iframe comes from markup you do not render with a component, such as CMS content, or you want no wrapper. |
|
|
15
|
+
|
|
16
|
+
Both keep the iframe's `src` out of the page until the category is allowed,
|
|
17
|
+
so the embed's host receives no request before consent.
|
|
18
|
+
|
|
19
|
+
## Gate an embed with ConsentGate
|
|
20
|
+
|
|
21
|
+
Wrap the iframe in the globally registered
|
|
22
|
+
[`ConsentGate`](https://c15t.com/docs/frameworks/nuxt/components/consent-gate):
|
|
23
|
+
|
|
24
|
+
```vue title="app/components/VideoEmbed.vue"
|
|
25
|
+
<template>
|
|
26
|
+
<ConsentGate category="measurement">
|
|
27
|
+
<iframe
|
|
28
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y"
|
|
29
|
+
title="YouTube video"
|
|
30
|
+
loading="lazy"
|
|
31
|
+
allowfullscreen
|
|
32
|
+
/>
|
|
33
|
+
<template #placeholder>
|
|
34
|
+
<p>Allow measurement to load this YouTube video.</p>
|
|
35
|
+
<ConsentPreferencesLink>Choose video permissions</ConsentPreferencesLink>
|
|
36
|
+
</template>
|
|
37
|
+
</ConsentGate>
|
|
38
|
+
</template>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The iframe is absent from the server HTML and the DOM until the visitor
|
|
42
|
+
allows measurement. The `placeholder` slot shows a message and a
|
|
43
|
+
[`ConsentPreferencesLink`](https://c15t.com/docs/frameworks/nuxt/components/consent-preferences-link)
|
|
44
|
+
until then. When the visitor withdraws measurement, Vue removes the iframe.
|
|
45
|
+
|
|
46
|
+
Pick the category that matches what the embed does. The example uses
|
|
47
|
+
measurement for YouTube because its player measures views. A map or chat
|
|
48
|
+
widget usually belongs under functionality or experience.
|
|
49
|
+
|
|
50
|
+
## Gate an iframe with the iframe blocker
|
|
51
|
+
|
|
52
|
+
The module runs the iframe blocker in the browser by default. Give an iframe
|
|
53
|
+
`data-src` instead of `src`, and a `data-category`:
|
|
54
|
+
|
|
55
|
+
```vue title="app/components/MapEmbed.vue"
|
|
56
|
+
<template>
|
|
57
|
+
<iframe
|
|
58
|
+
data-src="https://www.google.com/maps/embed?pb=YOUR_EMBED_ID"
|
|
59
|
+
data-category="functionality"
|
|
60
|
+
title="Store map"
|
|
61
|
+
/>
|
|
62
|
+
</template>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The server HTML has no `src`, so nothing loads before hydration. After
|
|
66
|
+
hydration, c15t sets `src` from `data-src` when the category is allowed and
|
|
67
|
+
removes it when the category is withdrawn. It watches the page, so iframes
|
|
68
|
+
added by later navigation are handled too. The iframe element stays in the
|
|
69
|
+
page while blocked, empty. Add `data-vendor` with a vendor ID to also block
|
|
70
|
+
the iframe while the visitor has turned that vendor off.
|
|
71
|
+
|
|
72
|
+
To turn the blocker off, set `iframeBlocker: false` in the module options.
|
|
73
|
+
|
|
74
|
+
## Verify
|
|
75
|
+
|
|
76
|
+
View the page source in a private window, under a policy that asks for
|
|
77
|
+
consent. No iframe has a `src` pointing at `youtube-nocookie.com` or
|
|
78
|
+
`google.com/maps`, and the Network tab shows no request to them. Allow the
|
|
79
|
+
category and save: the iframe loads. Withdraw it and save: the page reloads
|
|
80
|
+
without the embed. The [YouTube](../../integrations/youtube.md) and
|
|
81
|
+
[Google Maps](../../integrations/google-maps.md) guides cover sizing and titles.
|