@c15t/react 3.0.0-alpha.5 → 3.0.0-alpha.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.
- package/AGENTS.md +6 -4
- package/dist/component-hooks/use-headless-consent-ui.js +1 -1
- package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
- package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
- package/dist/components/iab-panel/index.js +1 -1
- package/dist/components/panel/index.js +1 -1
- package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
- package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -1
- package/dist/components/panel-trigger/panel-trigger.js +1 -1
- package/dist/components/preferences/index.js +1 -1
- package/dist/components/shared/ui/accordion/accordion.js +1 -1
- package/dist/components/shared/ui/collapsible/collapsible.js +1 -1
- package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
- package/dist/components/shared/ui/switch/switch.js +1 -1
- package/dist/components/shared/ui/tabs/tabs.js +1 -1
- package/dist/devtools-launcher.js +2 -0
- package/dist/devtools.js +1 -1
- package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
- package/dist/index-parts/core.js +1 -1
- package/dist/provider.js +1 -1
- package/dist/styles.tw3.css +27 -3
- package/dist/version.js +1 -1
- package/dist-types/component-hooks/use-headless-consent-ui.d.ts +4 -4
- package/dist-types/components/iab-panel/index.d.ts +0 -1
- package/dist-types/components/panel/index.d.ts +0 -1
- package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +5 -0
- package/dist-types/components/panel-trigger/panel-trigger.d.ts +0 -8
- package/dist-types/components/preferences/index.d.ts +0 -1
- package/dist-types/components/shared/ui/accordion/accordion.d.ts +0 -1
- package/dist-types/components/shared/ui/collapsible/collapsible.d.ts +0 -1
- package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +0 -1
- package/dist-types/components/shared/ui/switch/switch.d.ts +0 -1
- package/dist-types/components/shared/ui/tabs/tabs.d.ts +0 -1
- package/dist-types/devtools-launcher.d.ts +29 -0
- package/dist-types/hooks/use-iab-manager.d.ts +4 -4
- package/dist-types/hooks.d.ts +27 -5
- package/dist-types/index-parts/core.d.ts +1 -1
- package/dist-types/index.d.ts +1 -1
- package/dist-types/provider.d.ts +12 -1
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +6 -4
- package/docs/concepts/choose-your-setup.md +22 -5
- package/docs/concepts/consent-state.md +41 -0
- package/docs/concepts/data-fetching.md +34 -11
- package/docs/customization/recipes.md +4 -5
- package/docs/customization/slots.md +3 -2
- package/docs/customization/stylesheets.md +29 -39
- package/docs/customization/tailwind.md +5 -5
- package/docs/customization/tokens.md +1 -1
- package/docs/customization/translations.md +32 -5
- package/docs/frameworks/react/components/consent-dialog-trigger.md +5 -0
- package/docs/frameworks/react/components/consent-dialog.md +2 -1
- package/docs/frameworks/react/components/dev-tools.md +23 -0
- package/docs/frameworks/react/headless.md +4 -4
- package/docs/frameworks/react/hooks.md +10 -5
- package/docs/frameworks/react/iab.md +24 -18
- package/docs/frameworks/react/quickstart.md +77 -16
- package/docs/frameworks/react/rendering.md +82 -11
- package/docs/frameworks/react/scripts.md +8 -3
- package/docs/frameworks/react/upgrade-v3.md +596 -0
- package/docs/frameworks/react/vendor-consent.md +6 -0
- package/docs/guides/banner-experiments.md +26 -5
- package/docs/guides/troubleshooting.md +3 -2
- package/docs/integrations/overview.md +28 -27
- package/docs/integrations/sentry.md +416 -0
- package/docs/upgrade-v3.md +201 -314
- package/package.json +8 -8
|
@@ -107,6 +107,20 @@ the arm names in `arm` and `split`. The callbacks forward each impression and
|
|
|
107
107
|
choice to `window.dataLayer`; see
|
|
108
108
|
[send the events to your own analytics](#send-the-events-to-your-own-analytics-too).
|
|
109
109
|
|
|
110
|
+
Your flag provider returns a plain string. To check it before passing it as
|
|
111
|
+
`arm`, type it with `ExperimentArmName` from `c15t`, which is `control` plus
|
|
112
|
+
the experiment's arm names:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import type { ExperimentArmName } from 'c15t';
|
|
116
|
+
|
|
117
|
+
// 'control' | 'wall'
|
|
118
|
+
type BannerArm = ExperimentArmName<typeof bannerExperiment>;
|
|
119
|
+
|
|
120
|
+
const armFromFlag = (value: string | undefined): BannerArm | undefined =>
|
|
121
|
+
value === 'control' || value === 'wall' ? value : undefined;
|
|
122
|
+
```
|
|
123
|
+
|
|
110
124
|
Omit `arm` to let c15t pick, and set `split` to weight the arms:
|
|
111
125
|
|
|
112
126
|
```ts
|
|
@@ -137,6 +151,14 @@ no effect; remount the provider (a `key` in React) to switch experiments.
|
|
|
137
151
|
Assignment and arm validation load as a separate chunk, only on pages that
|
|
138
152
|
set `experiment`. A site without an experiment does not download them.
|
|
139
153
|
|
|
154
|
+
An experiment compares choices, so the banner has to ask about at least one
|
|
155
|
+
category. Under a rule with `scopeMode: 'permissive'`, the banner asks only
|
|
156
|
+
about the categories your site declares through `consentCategories`,
|
|
157
|
+
`scripts` or `vendors`. With none declared, it asks about none: accepting or rejecting records a notice
|
|
158
|
+
acknowledgement instead of a choice, `onChoiceRecorded` never fires, and the
|
|
159
|
+
experiment counts impressions without choices. Outside production, c15t logs
|
|
160
|
+
a warning the first time the banner shows an arm in that state.
|
|
161
|
+
|
|
140
162
|
### Astro
|
|
141
163
|
|
|
142
164
|
The Astro banner is server-rendered HTML that the browser only shows or
|
|
@@ -229,8 +251,7 @@ keeps their arm when you change the weights.
|
|
|
229
251
|
Resolve the flag where consent resolves and pass the experiment to
|
|
230
252
|
`resolveConsent`. The server reports the arm with `/init`, and the returned
|
|
231
253
|
state carries the experiment to `ConsentRoot`, so the client needs no
|
|
232
|
-
`experiment` option of its own.
|
|
233
|
-
layout of its `/experiment` route:
|
|
254
|
+
`experiment` option of its own. In Next.js, do this in the root layout:
|
|
234
255
|
|
|
235
256
|
```tsx title="app/layout.tsx"
|
|
236
257
|
import { resolveConsent } from 'c15t/next/server';
|
|
@@ -268,8 +289,8 @@ const RootLayout = ({ children }: { children: ReactNode }) => (
|
|
|
268
289
|
export default RootLayout;
|
|
269
290
|
```
|
|
270
291
|
|
|
271
|
-
|
|
272
|
-
|
|
292
|
+
`@/lib/flags` exports the flag above. `ExperimentConsent` is the `Consent`
|
|
293
|
+
wrapper from the
|
|
273
294
|
[App Router guide](https://c15t.com/docs/frameworks/next/app-router) with the event callbacks
|
|
274
295
|
from [send the events to your own analytics](#send-the-events-to-your-own-analytics-too)
|
|
275
296
|
in `options`. The layout awaits consent inside `<Suspense>`, as in
|
|
@@ -622,7 +643,7 @@ arm by the visitors whose reports carry that arm.
|
|
|
622
643
|
|
|
623
644
|
## Try it
|
|
624
645
|
|
|
625
|
-
Every example app under `
|
|
646
|
+
Every example app under `internals/fixtures/` runs this experiment outside the pages
|
|
626
647
|
the docs publish. Each page shows the assigned arm and the events the
|
|
627
648
|
callbacks sent. Adding `arm=wall` sets the arm the way a flag would, so the
|
|
628
649
|
page shows `assignedBy: host`. Run each command in the example's directory.
|
|
@@ -11,8 +11,9 @@ group: guides
|
|
|
11
11
|
Most problems show up in one of these places. Run them before changing code.
|
|
12
12
|
|
|
13
13
|
1. **Which c15t is running.** In the browser console, run `window.c15t`. v3
|
|
14
|
-
prints `{ version, pkg, mode }`, for example `mode: 'manifest'`
|
|
15
|
-
`@c15t/nextjs`. `
|
|
14
|
+
prints `{ version, pkg, mode, hosting }`, for example `mode: 'manifest'`
|
|
15
|
+
from `@c15t/nextjs`. `hosting` is `'inth'` or `'self-hosted'` once the
|
|
16
|
+
backend has answered `/init`, and `null` before then or without a backend. `undefined` means no c15t provider has mounted on this
|
|
16
17
|
page. A `window.c15tStore` object means the page runs v2. With the script
|
|
17
18
|
tag, `window.c15t` is the full browser API instead.
|
|
18
19
|
2. **The backend request.** In DevTools Network, filter by your backend URL.
|
|
@@ -47,33 +47,34 @@ kernel to control its DOM lifecycle. Embeds do not require `@c15t/integrations`.
|
|
|
47
47
|
|
|
48
48
|
## Analytics
|
|
49
49
|
|
|
50
|
-
| Integration | Helper | Category
|
|
51
|
-
| --------------------------------------------------------- | ------------------------ |
|
|
52
|
-
| [Google Tag](./google-tag.md) | `gtag` | `measurement` or `marketing`
|
|
53
|
-
| [Ahrefs Analytics](./ahrefs-analytics.md) | `ahrefsAnalytics` | `measurement`
|
|
54
|
-
| [Adobe Analytics](./adobe-analytics.md) | `adobeAnalytics` | `measurement`
|
|
55
|
-
| [Amplitude](./amplitude.md) | `amplitude` | `measurement`
|
|
56
|
-
| [Cloudflare Web Analytics](./cloudflare-web-analytics.md) | `cloudflareWebAnalytics` | `measurement`
|
|
57
|
-
| [Clearbit](./clearbit.md) | `clearbit` | `marketing`
|
|
58
|
-
| [Microsoft Clarity](./microsoft-clarity.md) | `clarity` | `measurement`
|
|
59
|
-
| [Databuddy](./databuddy.md) | `databuddy` | `measurement`
|
|
60
|
-
| [Fathom Analytics](./fathom-analytics.md) | `fathomAnalytics` | `measurement`
|
|
61
|
-
| [Heap](./heap.md) | `heap` | `measurement`
|
|
62
|
-
| [Matomo Analytics](./matomo-analytics.md) | `matomoAnalytics` | `measurement`
|
|
63
|
-
| [Mixpanel](./mixpanel-analytics.md) | `mixpanelAnalytics` | `measurement`
|
|
64
|
-
| [OneDollarStats](./one-dollar-stats.md) | `oneDollarStats` | `measurement`
|
|
65
|
-
| [Hotjar](./hotjar.md) | `hotjar` | `measurement`
|
|
66
|
-
| [Hightouch](./hightouch.md) | `hightouch` | `measurement`
|
|
67
|
-
| [LogRocket](./logrocket.md) | `logRocket` | `measurement`
|
|
68
|
-
| [Plausible Analytics](./plausible-analytics.md) | `plausibleAnalytics` | `measurement`
|
|
69
|
-
| [PostHog](./posthog.md) | `posthog` | `measurement`
|
|
70
|
-
| [Promptwatch](./promptwatch.md) | `promptwatch` | `measurement`
|
|
71
|
-
| [Pirsch](./pirsch.md) | `pirsch` | `measurement`
|
|
72
|
-
| [RudderStack](./rudderstack.md) | `rudderstack` | `measurement`
|
|
73
|
-
| [Segment](./segment.md) | `segment` | `measurement`
|
|
74
|
-
| [
|
|
75
|
-
| [
|
|
76
|
-
| [
|
|
50
|
+
| Integration | Helper | Category | Loading behavior |
|
|
51
|
+
| --------------------------------------------------------- | ------------------------ | ----------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
52
|
+
| [Google Tag](./google-tag.md) | `gtag` | `measurement` or `marketing` | Always loads; signals Google consent |
|
|
53
|
+
| [Ahrefs Analytics](./ahrefs-analytics.md) | `ahrefsAnalytics` | `measurement` | Waits for effective permission |
|
|
54
|
+
| [Adobe Analytics](./adobe-analytics.md) | `adobeAnalytics` | `measurement` | Waits for effective permission |
|
|
55
|
+
| [Amplitude](./amplitude.md) | `amplitude` | `measurement` | Waits for effective permission; opts the SDK out on revocation |
|
|
56
|
+
| [Cloudflare Web Analytics](./cloudflare-web-analytics.md) | `cloudflareWebAnalytics` | `measurement` | Waits for effective permission |
|
|
57
|
+
| [Clearbit](./clearbit.md) | `clearbit` | `marketing` | Waits for effective permission |
|
|
58
|
+
| [Microsoft Clarity](./microsoft-clarity.md) | `clarity` | `measurement` | Gated initially; keeps the SDK and signals storage consent |
|
|
59
|
+
| [Databuddy](./databuddy.md) | `databuddy` | `measurement` | Always loads; switches the SDK's disabled flag |
|
|
60
|
+
| [Fathom Analytics](./fathom-analytics.md) | `fathomAnalytics` | `measurement` | Waits for effective permission |
|
|
61
|
+
| [Heap](./heap.md) | `heap` | `measurement` | Waits for effective permission |
|
|
62
|
+
| [Matomo Analytics](./matomo-analytics.md) | `matomoAnalytics` | `measurement` | Gated by default; optional consent mode |
|
|
63
|
+
| [Mixpanel](./mixpanel-analytics.md) | `mixpanelAnalytics` | `measurement` | Always loads; calls opt-in and opt-out APIs |
|
|
64
|
+
| [OneDollarStats](./one-dollar-stats.md) | `oneDollarStats` | `measurement` | Waits for effective permission |
|
|
65
|
+
| [Hotjar](./hotjar.md) | `hotjar` | `measurement` | Waits for effective permission |
|
|
66
|
+
| [Hightouch](./hightouch.md) | `hightouch` | `measurement` | Waits for effective permission |
|
|
67
|
+
| [LogRocket](./logrocket.md) | `logRocket` | `measurement` | Waits for effective permission |
|
|
68
|
+
| [Plausible Analytics](./plausible-analytics.md) | `plausibleAnalytics` | `measurement` | Waits for effective permission |
|
|
69
|
+
| [PostHog](./posthog.md) | `posthog` | `measurement` | Configurable; defaults to always loading and calling opt-in and opt-out APIs |
|
|
70
|
+
| [Promptwatch](./promptwatch.md) | `promptwatch` | `measurement` | Waits for effective permission |
|
|
71
|
+
| [Pirsch](./pirsch.md) | `pirsch` | `measurement` | Waits for effective permission |
|
|
72
|
+
| [RudderStack](./rudderstack.md) | `rudderstack` | `measurement` | Gated by default; optional destination consent mode |
|
|
73
|
+
| [Segment](./segment.md) | `segment` | `measurement` | Waits for effective permission |
|
|
74
|
+
| [Sentry](./sentry.md) | `sentry` | `necessary`; Replay and user data `measurement` | Configurable; defaults to always loading and loads Replay after permission |
|
|
75
|
+
| [Rybbit Analytics](./rybbit-analytics.md) | `rybbitAnalytics` | `measurement` | Waits for effective permission |
|
|
76
|
+
| [Umami Analytics](./umami-analytics.md) | `umamiAnalytics` | `measurement` | Waits for effective permission |
|
|
77
|
+
| [Vercel Analytics](./vercel-analytics.md) | `vercelAnalytics` | `measurement` | Waits for effective permission |
|
|
77
78
|
|
|
78
79
|
## Chat and support
|
|
79
80
|
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Sentry
|
|
3
|
+
description: Configure Sentry's CDN or your own SDK with consent for error
|
|
4
|
+
monitoring, Session Replay and user data.
|
|
5
|
+
icon: sentry
|
|
6
|
+
group: integrations
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Configure Sentry
|
|
10
|
+
|
|
11
|
+
Remove any existing Sentry Loader Script or CDN tags, then add this configuration.
|
|
12
|
+
Errors run before consent by default; Replay and SDK data collection wait for
|
|
13
|
+
`measurement`.
|
|
14
|
+
|
|
15
|
+
| Package manager | Command |
|
|
16
|
+
| :-------------- | :------------------------------------- |
|
|
17
|
+
| npm | `npm install @c15t/integrations@alpha` |
|
|
18
|
+
| pnpm | `pnpm add @c15t/integrations@alpha` |
|
|
19
|
+
| yarn | `yarn add @c15t/integrations@alpha` |
|
|
20
|
+
| bun | `bun add @c15t/integrations@alpha` |
|
|
21
|
+
|
|
22
|
+
```ts title="src/consent-scripts.ts"
|
|
23
|
+
import { sentry } from '@c15t/integrations/sentry';
|
|
24
|
+
|
|
25
|
+
export const scripts = [
|
|
26
|
+
sentry({
|
|
27
|
+
dsn: 'https://your-key@o0.ingest.sentry.io/0',
|
|
28
|
+
initOptions: {
|
|
29
|
+
release: 'my-app@1.0.0',
|
|
30
|
+
replaysOnErrorSampleRate: 1,
|
|
31
|
+
replaysSessionSampleRate: 0.1,
|
|
32
|
+
tracesSampleRate: 0.1,
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
];
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The default SDK is 11.4.0 with subresource integrity: `bundle.min.js`, or
|
|
39
|
+
`bundle.tracing.min.js` when traces are sampled. `replay.min.js` loads after
|
|
40
|
+
consent when a Replay sample rate is above 0.
|
|
41
|
+
|
|
42
|
+
## Register the scripts
|
|
43
|
+
|
|
44
|
+
Complete your [framework quickstart](https://c15t.com/docs/frameworks) first. Keep its Inth
|
|
45
|
+
endpoint, policy, styles and consent UI. Remove the vendor's original script,
|
|
46
|
+
SDK initializer or tag-manager entry, so the vendor loads only through c15t.
|
|
47
|
+
|
|
48
|
+
The vendor pages put the helper in `src/consent-scripts.ts`. If your framework
|
|
49
|
+
quickstart already created a scripts file, such as `lib/scripts.ts` in the
|
|
50
|
+
Next.js guide, add the helper to that array instead of creating a second file.
|
|
51
|
+
The `scripts` export is a configuration, not an initializer. Add it to the c15t provider you already have, at the registration
|
|
52
|
+
point for your framework below. These are edits to that provider, not a second
|
|
53
|
+
provider.
|
|
54
|
+
|
|
55
|
+
**Next.js**
|
|
56
|
+
|
|
57
|
+
Import the configuration into the client boundary from your router guide:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { ConsentRoot } from 'c15t/next';
|
|
61
|
+
import { scripts } from './consent-scripts';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Keep the server-resolved `state` and shared `consentConfig` from your
|
|
65
|
+
router guide. Its manifest, init and save URLs stay in effect. Add
|
|
66
|
+
`scripts` as a top-level prop on the existing root:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
<ConsentRoot state={state} config={consentConfig} scripts={scripts}>
|
|
70
|
+
{children}
|
|
71
|
+
</ConsentRoot>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
App Router, Pages Router and static export all use this `ConsentRoot` in
|
|
75
|
+
the `'use client'` wrapper `components/consent.tsx`. Keep `scripts` there,
|
|
76
|
+
because a Server Component cannot pass script callbacks to it. See
|
|
77
|
+
[Next.js scripts and embeds](https://c15t.com/docs/frameworks/next/scripts).
|
|
78
|
+
|
|
79
|
+
**TanStack Start**
|
|
80
|
+
|
|
81
|
+
Import the configuration into your root route and pass it to the existing
|
|
82
|
+
`ConsentRoot` as a top-level prop. Keep the loader, `backendURL` and
|
|
83
|
+
`initRoute` from the [TanStack Start quickstart](https://c15t.com/docs/frameworks/tanstack-start/quickstart):
|
|
84
|
+
|
|
85
|
+
```tsx title="src/routes/__root.tsx"
|
|
86
|
+
import { scripts } from '../consent-scripts';
|
|
87
|
+
|
|
88
|
+
<ConsentRoot
|
|
89
|
+
state={consent}
|
|
90
|
+
backendURL={backendURL}
|
|
91
|
+
initRoute={false}
|
|
92
|
+
scripts={scripts}
|
|
93
|
+
>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Import vendor helpers in the root route module, not in a server function.
|
|
97
|
+
A server function's return value must be serializable, and script
|
|
98
|
+
configurations carry callbacks. See
|
|
99
|
+
[TanStack Start scripts](https://c15t.com/docs/frameworks/tanstack-start/scripts).
|
|
100
|
+
|
|
101
|
+
**React**
|
|
102
|
+
|
|
103
|
+
Add the configuration to the existing `ConsentProvider` options, next to
|
|
104
|
+
`mode`:
|
|
105
|
+
|
|
106
|
+
```tsx title="src/consent.tsx"
|
|
107
|
+
import { scripts } from './consent-scripts';
|
|
108
|
+
|
|
109
|
+
<ConsentProvider options={{ mode, scripts }}>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`mode` is the `hosted({ url: 'https://your-project.inth.app' })` value
|
|
113
|
+
from the [React quickstart](../frameworks/react/quickstart.md). Keep the banner,
|
|
114
|
+
dialog and preferences link inside the provider. See
|
|
115
|
+
[React scripts and embeds](../frameworks/react/scripts.md).
|
|
116
|
+
|
|
117
|
+
**Nuxt**
|
|
118
|
+
|
|
119
|
+
Register the scripts under the `c15t` key in `app/app.config.ts`. Adjust the
|
|
120
|
+
relative import to where you created `consent-scripts.ts`:
|
|
121
|
+
|
|
122
|
+
```ts title="app/app.config.ts"
|
|
123
|
+
import { scripts } from '../src/consent-scripts';
|
|
124
|
+
|
|
125
|
+
export default defineAppConfig({
|
|
126
|
+
c15t: { scripts },
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The Nuxt module merges this over its options in `nuxt.config.ts` and starts
|
|
131
|
+
one script loader in the browser after hydration, once it has applied the
|
|
132
|
+
visitor's stored choice and privacy signals. Keep `scripts` out of
|
|
133
|
+
`nuxt.config.ts`, which reaches the browser as JSON and drops the vendor
|
|
134
|
+
callbacks. Write the vendor IDs into `consent-scripts.ts`. See
|
|
135
|
+
[Nuxt scripts and embeds](https://c15t.com/docs/frameworks/nuxt/scripts).
|
|
136
|
+
|
|
137
|
+
**Vue**
|
|
138
|
+
|
|
139
|
+
Pass the scripts to the existing `c15tVue` plugin call in `src/main.ts`:
|
|
140
|
+
|
|
141
|
+
```ts title="src/main.ts"
|
|
142
|
+
import { scripts } from './consent-scripts';
|
|
143
|
+
|
|
144
|
+
app.use(c15tVue, {
|
|
145
|
+
backendURL: 'https://your-project.inth.app',
|
|
146
|
+
scripts,
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Keep your existing backend URL and other options. The plugin starts one
|
|
151
|
+
script loader when the app mounts, after it has applied the visitor's stored
|
|
152
|
+
choice. Do not also call `createScriptLoader` from a component. See
|
|
153
|
+
[Vue scripts and embeds](https://c15t.com/docs/frameworks/vue/scripts).
|
|
154
|
+
|
|
155
|
+
**Astro**
|
|
156
|
+
|
|
157
|
+
Add the scripts to the client entrypoint from the
|
|
158
|
+
[Astro quickstart](https://c15t.com/docs/frameworks/astro/quickstart), the module that the
|
|
159
|
+
integration's `clientEntrypoint` option names. Keep `mode`, `ui` and the
|
|
160
|
+
framework integration in `astro.config.mjs` as they are. If the module
|
|
161
|
+
already exports scripts, combine the two arrays.
|
|
162
|
+
|
|
163
|
+
```ts title="src/consent-client.ts"
|
|
164
|
+
import type { C15tClientOptionsExtension } from 'c15t/astro';
|
|
165
|
+
import { scripts } from './consent-scripts';
|
|
166
|
+
|
|
167
|
+
export default { scripts } satisfies C15tClientOptionsExtension;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Vendor helpers contain callbacks, and the integration options in
|
|
171
|
+
`astro.config.mjs` are serialized into the page, so do not put helpers in
|
|
172
|
+
the integration's `scripts` option. The integration passes the client
|
|
173
|
+
entrypoint to the one runtime every page shares, including across
|
|
174
|
+
`ClientRouter` navigation.
|
|
175
|
+
|
|
176
|
+
**Svelte**
|
|
177
|
+
|
|
178
|
+
Import the scripts in the component that owns your existing provider and
|
|
179
|
+
pass them as a top-level prop:
|
|
180
|
+
|
|
181
|
+
```svelte title="src/App.svelte"
|
|
182
|
+
<script lang="ts">
|
|
183
|
+
import { ConsentManagerProvider, hosted } from '@c15t/svelte';
|
|
184
|
+
import { scripts } from './consent-scripts';
|
|
185
|
+
|
|
186
|
+
const mode = hosted({ url: 'https://your-project.inth.app' });
|
|
187
|
+
</script>
|
|
188
|
+
|
|
189
|
+
<ConsentManagerProvider {mode} {scripts}>
|
|
190
|
+
<!-- Keep your application, consent UI and preferences link here. -->
|
|
191
|
+
</ConsentManagerProvider>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Retain the styles and consent UI from the [Svelte quickstart](https://c15t.com/docs/frameworks/svelte/quickstart).
|
|
195
|
+
The provider owns the loader and disposes it on unmount.
|
|
196
|
+
|
|
197
|
+
**SvelteKit**
|
|
198
|
+
|
|
199
|
+
Add the scripts to the existing root layout provider. Keep the server load
|
|
200
|
+
and its serializable prefetch data from the [SvelteKit quickstart](https://c15t.com/docs/frameworks/sveltekit/quickstart).
|
|
201
|
+
|
|
202
|
+
```svelte title="src/routes/+layout.svelte"
|
|
203
|
+
<script lang="ts">
|
|
204
|
+
import { ConsentManagerProvider, hosted } from '@c15t/svelte';
|
|
205
|
+
import { scripts } from '../consent-scripts';
|
|
206
|
+
|
|
207
|
+
let { children, data } = $props();
|
|
208
|
+
const mode = hosted({ url: 'https://your-project.inth.app' });
|
|
209
|
+
</script>
|
|
210
|
+
|
|
211
|
+
<ConsentManagerProvider {mode} {scripts} prefetch={data.prefetch}>
|
|
212
|
+
{@render children()}
|
|
213
|
+
<!-- Keep your consent UI and preferences link here. -->
|
|
214
|
+
</ConsentManagerProvider>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Import vendor helpers in the layout component, not in `+layout.server.ts`:
|
|
218
|
+
a server load cannot send functions to the browser. Prerendered, static and
|
|
219
|
+
SPA-mode pages use the same `scripts` prop. If you pass an externally owned
|
|
220
|
+
`runtime` to the provider, register scripts when creating that runtime instead.
|
|
221
|
+
|
|
222
|
+
**HTML**
|
|
223
|
+
|
|
224
|
+
The helpers in `@c15t/integrations` are ES modules that need a bundler. On a
|
|
225
|
+
page that loads the c15t script tag, paste the vendor's own snippet instead
|
|
226
|
+
and keep it inert until its category is allowed:
|
|
227
|
+
|
|
228
|
+
```html
|
|
229
|
+
<script type="text/plain" data-c15t-category="measurement">
|
|
230
|
+
// The vendor's snippet, unchanged
|
|
231
|
+
</script>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Use the category this guide names for the vendor. c15t runs the snippet
|
|
235
|
+
once that category is allowed, and reloads the page when the visitor
|
|
236
|
+
withdraws it. Helper options on this page, such as `loadMode`, do not apply
|
|
237
|
+
to a pasted snippet. See [HTML scripts](https://c15t.com/docs/frameworks/html/scripts).
|
|
238
|
+
|
|
239
|
+
**JavaScript**
|
|
240
|
+
|
|
241
|
+
Pass the scripts to `init()` from `@c15t/browser`, next to your backend
|
|
242
|
+
URL:
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import { init } from '@c15t/browser';
|
|
246
|
+
import { scripts } from './consent-scripts';
|
|
247
|
+
|
|
248
|
+
const consent = init({
|
|
249
|
+
backendURL: 'https://your-project.inth.app',
|
|
250
|
+
scripts,
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Keep the backend URL from your quickstart. With
|
|
255
|
+
`createConsentRuntime` from `c15t/runtime`, pass `scripts` to it instead.
|
|
256
|
+
A kernel you create yourself needs a loader from
|
|
257
|
+
`c15t/modules/script-loader`. Attach one loader per kernel. See
|
|
258
|
+
[JavaScript scripts](https://c15t.com/docs/frameworks/javascript/scripts).
|
|
259
|
+
|
|
260
|
+
## Use your own Sentry SDK
|
|
261
|
+
|
|
262
|
+
For `@sentry/*` 10.67.0 or later, pass your SDK's functions instead of `dsn`.
|
|
263
|
+
Keep Replay in a separate module so it can load after consent:
|
|
264
|
+
|
|
265
|
+
```ts title="src/sentry-replay.ts"
|
|
266
|
+
import { replayIntegration } from '@sentry/browser';
|
|
267
|
+
|
|
268
|
+
export const createReplay = () =>
|
|
269
|
+
replayIntegration({ maskAllText: true, blockAllMedia: true });
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
In `src/consent-scripts.ts`, replace the CDN configuration with:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { getClient, init, setUser } from '@sentry/browser';
|
|
276
|
+
|
|
277
|
+
sentry({
|
|
278
|
+
getClient,
|
|
279
|
+
init,
|
|
280
|
+
setUser,
|
|
281
|
+
initOptions: {
|
|
282
|
+
dsn: 'https://your-key@o0.ingest.sentry.io/0',
|
|
283
|
+
replaysSessionSampleRate: 0.1,
|
|
284
|
+
replaysOnErrorSampleRate: 1,
|
|
285
|
+
},
|
|
286
|
+
replay: {
|
|
287
|
+
load: () => import('./sentry-replay').then((m) => m.createReplay()),
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Passing `init` protects startup captures before other SDK integrations run.
|
|
293
|
+
Custom initializers must forward the supplied options to `Sentry.init`, including
|
|
294
|
+
after a dynamic import. Avoid initialization as an import side effect.
|
|
295
|
+
|
|
296
|
+
Use named imports from the same Sentry package throughout your app. Next.js and
|
|
297
|
+
Vite split dynamically imported Replay into its own chunk; esbuild and Bun keep
|
|
298
|
+
it in the main bundle. A main-bundle `Sentry.replayIntegration` reference through
|
|
299
|
+
`import * as Sentry` also prevents the split.
|
|
300
|
+
|
|
301
|
+
### If Sentry already initializes independently
|
|
302
|
+
|
|
303
|
+
c15t applies filters once it finds the client; it cannot retract earlier data.
|
|
304
|
+
For startup protection, disable collection and remove event users in your own
|
|
305
|
+
`Sentry.init` call. With SDK 10.67, omit `queues`:
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
// Include these options in your app's Sentry.init call.
|
|
309
|
+
maxBreadcrumbs: 0,
|
|
310
|
+
dataCollection: {
|
|
311
|
+
userInfo: false,
|
|
312
|
+
cookies: false,
|
|
313
|
+
httpHeaders: { request: false, response: false },
|
|
314
|
+
httpBodies: [],
|
|
315
|
+
urlQueryParams: false,
|
|
316
|
+
graphQL: { document: false, variables: false },
|
|
317
|
+
genAI: { inputs: false, outputs: false },
|
|
318
|
+
databaseQueryData: false,
|
|
319
|
+
queues: false,
|
|
320
|
+
stackFrameVariables: false,
|
|
321
|
+
frameContextLines: 0,
|
|
322
|
+
},
|
|
323
|
+
beforeSend(event) {
|
|
324
|
+
delete event.user;
|
|
325
|
+
delete event.request;
|
|
326
|
+
return event;
|
|
327
|
+
},
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
These settings keep collection and event users disabled after consent too.
|
|
331
|
+
To restore event users, check effective c15t permission in `beforeSend`. Wait
|
|
332
|
+
until the adapter is registered before assigning scope users or capturing logs
|
|
333
|
+
and metrics.
|
|
334
|
+
|
|
335
|
+
## Options
|
|
336
|
+
|
|
337
|
+
| Option | Default | Behavior |
|
|
338
|
+
| ----------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
|
|
339
|
+
| `dsn` | Required to load Sentry | Your project's DSN. c15t loads the SDK and calls `Sentry.init`. |
|
|
340
|
+
| `initOptions` | `{}` | Passed to `Sentry.init` with c15t's consent integration first. CDN `integrations` callbacks can access `window.Sentry`. |
|
|
341
|
+
| `version` | `'11.4.0'` | SDK version to load. Another version loads without subresource integrity. |
|
|
342
|
+
| `tracing` | `true` when traces are sampled | Loads the tracing bundle and adds `browserTracingIntegration()`. |
|
|
343
|
+
| `replay` | Loads CDN Replay when sampled | CDN: `{ category, options }` or `false`. SDK: `{ category, load }`. |
|
|
344
|
+
| `getClient` | Required for your own SDK | Your SDK's `getClient`. Supports initialization after c15t starts. |
|
|
345
|
+
| `setUser` | None | `setUser` from your Sentry SDK. Called with `null` while user data is not allowed. Required with `pii.user`. |
|
|
346
|
+
| `init` | None | Your SDK's `init`, called once when `loadMode` allows. Required for SDK mode with `'after-consent'`. |
|
|
347
|
+
| `replay.load` | None | With your own SDK, returns a new `replayIntegration()`. Called once, the first time Replay is allowed. |
|
|
348
|
+
| `loadMode` | `'always'` | When error monitoring runs. See the table below. |
|
|
349
|
+
| `replay.category` | `'measurement'` | Consent condition for Replay. `pii.category` must also be allowed. |
|
|
350
|
+
| `pii.category` | `'measurement'` | Consent condition for SDK data collection and user data. `'necessary'` permits it while the Sentry vendor is enabled. |
|
|
351
|
+
| `pii.user` | None | Returns the user to set when user data becomes allowed. |
|
|
352
|
+
| `onError` | Report to an available Sentry client | Receives CDN and Replay lifecycle failures. Set it to report CDN download failures before Sentry exists. |
|
|
353
|
+
|
|
354
|
+
Replay's category is registered only with an SDK `replay.load` function or
|
|
355
|
+
enabled CDN Replay with a positive sample rate.
|
|
356
|
+
|
|
357
|
+
## Loading and revocation
|
|
358
|
+
|
|
359
|
+
| `loadMode` | When Sentry runs | On withdrawal without a reload |
|
|
360
|
+
| ----------------- | ----------------------------------- | ----------------------------------------------------------- |
|
|
361
|
+
| `'always'` | On every page, before consent | Replay stops; errors keep reaching Sentry without user data |
|
|
362
|
+
| `'after-consent'` | Only while `measurement` is allowed | Sentry stops sending anything |
|
|
363
|
+
|
|
364
|
+
Replay needs both `replay.category` and `pii.category`, because recordings
|
|
365
|
+
include page URLs with queries and fragments. Keep Replay out of `Sentry.init`:
|
|
366
|
+
`replaysOnErrorSampleRate` can buffer recordings before consent. On denial or
|
|
367
|
+
final removal, c15t stops Replay without flushing, discards queued uploads and
|
|
368
|
+
blocks restarts. Data already sent stays in Sentry; a later grant preserves
|
|
369
|
+
the original sampling decision.
|
|
370
|
+
|
|
371
|
+
While PII is denied, c15t disables supported `dataCollection` settings and IP
|
|
372
|
+
inference. Before sending, it removes user fields, feedback contact details,
|
|
373
|
+
visitor identifiers in sessions, request data and URL queries/fragments from
|
|
374
|
+
supported events, spans, logs, metrics and breadcrumbs, including retained
|
|
375
|
+
breadcrumbs. A grant restores your resolved collection settings and allowlists.
|
|
376
|
+
Tracing itself is not gated. Custom messages, tags, extras, attachments and
|
|
377
|
+
URL paths remain diagnostics, so keep sensitive values out of them.
|
|
378
|
+
|
|
379
|
+
The vendor slug is `sentry`. Turning it off denies Replay and PII, plus errors
|
|
380
|
+
in `'after-consent'` mode. See [vendor consent](./overview.md#vendor-switches-and-cookie-cleanup).
|
|
381
|
+
|
|
382
|
+
### Shared clients and configuration changes
|
|
383
|
+
|
|
384
|
+
Keep callback functions stable across renders. Equal options and callbacks
|
|
385
|
+
reuse the SDK bundle and active recording. Changed CDN options initialize a
|
|
386
|
+
client with the new DSN and settings, while reusing the page's single Replay
|
|
387
|
+
recorder. Its original Replay options and sampling decision last until reload.
|
|
388
|
+
|
|
389
|
+
Every active configuration targeting one client must allow each feature;
|
|
390
|
+
removing a loader releases its restriction and preserves surviving loaders and
|
|
391
|
+
downloads. Final removal denies PII and stops Replay, and also disables errors
|
|
392
|
+
in `'after-consent'` mode. An app-initiated `Sentry.close()` stays closed after
|
|
393
|
+
a grant. Separate clients have separate permissions.
|
|
394
|
+
|
|
395
|
+
## Content security policy
|
|
396
|
+
|
|
397
|
+
Allow `worker-src blob:` for Replay and your ingest or tunnel endpoint in
|
|
398
|
+
`connect-src`. CDN mode also needs `https://browser.sentry-cdn.com` in
|
|
399
|
+
`script-src`. The loader or provider's `nonce` applies to both CDN bundles;
|
|
400
|
+
a per-script nonce overrides it.
|
|
401
|
+
|
|
402
|
+
## Verify Sentry
|
|
403
|
+
|
|
404
|
+
Test in a private window with an opt-in policy and
|
|
405
|
+
`replaysSessionSampleRate: 1`. Filter DevTools Network by `sentry`.
|
|
406
|
+
|
|
407
|
+
1. Load the page. `bundle.min.js` loads and errors reach Sentry, without `user`.
|
|
408
|
+
No `replay.min.js` or `replay_recording` request is sent. With
|
|
409
|
+
`loadMode: 'after-consent'`, nothing loads.
|
|
410
|
+
2. Allow measurement. Without a reload, Replay loads and `replay_recording`
|
|
411
|
+
requests start.
|
|
412
|
+
3. Turn measurement off and save. c15t reloads the page, and no replay request
|
|
413
|
+
follows.
|
|
414
|
+
|
|
415
|
+
See the [consent verification guide](../guides/verify-consent.md) for
|
|
416
|
+
navigation, expiry and hosting checks.
|