@c15t/react 3.1.0 → 3.2.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/AGENTS.md +11 -4
  2. package/README.md +2 -2
  3. package/dist/component-hooks/use-consent-gate.js +2 -0
  4. package/dist/component-hooks/use-consent-labels.js +2 -0
  5. package/dist/component-hooks/use-consent-text-direction.js +2 -0
  6. package/dist/component-hooks/use-vendors-listed-under.js +2 -0
  7. package/dist/components/consent-gate/atoms.js +1 -1
  8. package/dist/components/consent-gate/consent-gate.js +1 -1
  9. package/dist/components/iab-prompt/atoms/root.js +1 -1
  10. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  11. package/dist/components/preferences/atoms/button.js +1 -1
  12. package/dist/components/preferences/atoms/vendor-list.js +1 -1
  13. package/dist/components/prompt/atoms/root.js +1 -1
  14. package/dist/components/prompt/components.js +1 -1
  15. package/dist/components/prompt/policy-actions.js +1 -1
  16. package/dist/components/prompt/prompt.js +1 -1
  17. package/dist/components/prompt/surface-context.js +1 -1
  18. package/dist/components/prompt/use-banner-copy.js +1 -1
  19. package/dist/components/shared/streamed-surface.js +1 -1
  20. package/dist/headless.js +1 -1
  21. package/dist/index-parts/offline.js +1 -1
  22. package/dist/modes.js +1 -1
  23. package/dist/transports/offline.js +1 -1
  24. package/dist/version.js +1 -1
  25. package/dist-types/component-hooks/use-consent-gate.d.ts +102 -0
  26. package/dist-types/component-hooks/use-consent-labels.d.ts +32 -0
  27. package/dist-types/component-hooks/use-consent-text-direction.d.ts +22 -0
  28. package/dist-types/component-hooks/use-vendors-listed-under.d.ts +30 -0
  29. package/dist-types/components/prompt/atoms/root.d.ts +3 -3
  30. package/dist-types/components/prompt/prompt.d.ts +4 -3
  31. package/dist-types/components/prompt/surface-context.d.ts +1 -14
  32. package/dist-types/components/prompt/use-banner-copy.d.ts +8 -3
  33. package/dist-types/components/shared/streamed-surface.d.ts +47 -10
  34. package/dist-types/headless.d.ts +10 -1
  35. package/dist-types/hooks/use-iab-policy-guard.d.ts +17 -1
  36. package/dist-types/hooks/use-scroll-lock.d.ts +16 -2
  37. package/dist-types/index-parts/offline.d.ts +1 -1
  38. package/dist-types/modes.d.ts +1 -1
  39. package/dist-types/module-hooks/script-loader.d.ts +9 -0
  40. package/dist-types/provider.d.ts +3 -3
  41. package/dist-types/transports/offline.d.ts +2 -2
  42. package/dist-types/version.d.ts +1 -1
  43. package/docs/README.md +11 -4
  44. package/docs/concepts/choose-your-setup.md +9 -9
  45. package/docs/concepts/consent-state.md +81 -9
  46. package/docs/concepts/data-fetching.md +30 -23
  47. package/docs/concepts/modes.md +85 -35
  48. package/docs/customization/recipes.md +4 -4
  49. package/docs/customization/stylesheets.md +3 -3
  50. package/docs/customization/tailwind.md +17 -34
  51. package/docs/customization/translations.md +18 -12
  52. package/docs/frameworks/react/clear-on-revocation.md +3 -2
  53. package/docs/frameworks/react/components/consent-banner.md +3 -3
  54. package/docs/frameworks/react/components/consent-provider.md +29 -29
  55. package/docs/frameworks/react/gpp.md +1 -1
  56. package/docs/frameworks/react/headless.md +146 -19
  57. package/docs/frameworks/react/iab.md +8 -1
  58. package/docs/frameworks/react/quickstart.md +1 -1
  59. package/docs/frameworks/react/rendering.md +1 -1
  60. package/docs/frameworks/react/translations.md +9 -9
  61. package/docs/frameworks/react/troubleshooting.md +1 -1
  62. package/docs/frameworks/react/upgrade-v3.md +39 -32
  63. package/docs/frameworks/react/vendor-consent.md +5 -0
  64. package/docs/guides/banner-experiments.md +3 -3
  65. package/docs/guides/troubleshooting.md +3 -2
  66. package/docs/integrations/adobe-analytics.md +2 -2
  67. package/docs/integrations/ahrefs-analytics.md +2 -2
  68. package/docs/integrations/amplitude.md +2 -2
  69. package/docs/integrations/clay.md +305 -0
  70. package/docs/integrations/clearbit.md +2 -2
  71. package/docs/integrations/cloudflare-web-analytics.md +2 -2
  72. package/docs/integrations/cloudflare-zaraz.md +2 -2
  73. package/docs/integrations/crisp.md +2 -2
  74. package/docs/integrations/databuddy.md +2 -2
  75. package/docs/integrations/fathom-analytics.md +2 -2
  76. package/docs/integrations/front-chat.md +2 -2
  77. package/docs/integrations/google-maps.md +2 -2
  78. package/docs/integrations/google-tag-manager.md +59 -2
  79. package/docs/integrations/google-tag.md +2 -2
  80. package/docs/integrations/heap.md +2 -2
  81. package/docs/integrations/hightouch.md +2 -2
  82. package/docs/integrations/hotjar.md +40 -26
  83. package/docs/integrations/intercom.md +2 -2
  84. package/docs/integrations/klaviyo.md +2 -2
  85. package/docs/integrations/linkedin-insights.md +2 -2
  86. package/docs/integrations/logrocket.md +2 -2
  87. package/docs/integrations/matomo-analytics.md +2 -2
  88. package/docs/integrations/meta-pixel.md +2 -2
  89. package/docs/integrations/microsoft-clarity.md +2 -2
  90. package/docs/integrations/microsoft-uet.md +2 -2
  91. package/docs/integrations/mixpanel-analytics.md +2 -2
  92. package/docs/integrations/one-dollar-stats.md +2 -2
  93. package/docs/integrations/openai-pixel.md +2 -2
  94. package/docs/integrations/other-ads-and-pixels.md +94 -0
  95. package/docs/integrations/other-analytics.md +108 -0
  96. package/docs/integrations/other-chat-and-support.md +112 -0
  97. package/docs/integrations/other-email-and-sms.md +156 -0
  98. package/docs/integrations/other-experience.md +222 -0
  99. package/docs/integrations/overview.md +55 -21
  100. package/docs/integrations/pinterest-tag.md +2 -2
  101. package/docs/integrations/pirsch.md +2 -2
  102. package/docs/integrations/plausible-analytics.md +2 -2
  103. package/docs/integrations/posthog.md +2 -2
  104. package/docs/integrations/promptwatch.md +2 -2
  105. package/docs/integrations/reddit-pixel.md +2 -2
  106. package/docs/integrations/rudderstack.md +2 -2
  107. package/docs/integrations/rybbit-analytics.md +2 -2
  108. package/docs/integrations/segment.md +2 -2
  109. package/docs/integrations/sentry.md +2 -2
  110. package/docs/integrations/shopify.md +323 -0
  111. package/docs/integrations/snapchat-pixel.md +2 -2
  112. package/docs/integrations/tiktok-pixel.md +2 -2
  113. package/docs/integrations/umami-analytics.md +2 -2
  114. package/docs/integrations/vercel-analytics.md +2 -2
  115. package/docs/integrations/vercel-speed-insights.md +7 -2
  116. package/docs/integrations/x-pixel.md +2 -2
  117. package/docs/integrations/youtube.md +2 -2
  118. package/package.json +10 -32
  119. package/dist/components/prompt/resolve-banner-primary-actions.js +0 -1
  120. package/dist-types/components/prompt/resolve-banner-primary-actions.d.ts +0 -14
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: Other email, SMS and marketing automation tools
3
+ description: Consent-gated helpers for email, SMS and marketing automation tools
4
+ that don't need a full guide.
5
+ icon: mail
6
+ group: integrations
7
+ ---
8
+
9
+ Each helper below loads its vendor only after the visitor allows the listed consent category. For setup, see [Integrations overview](./overview.md).
10
+
11
+ ## Attentive
12
+
13
+ Attentive shows SMS and email signup units and tracks on-site events for its marketing messages. Waits for `marketing` consent.
14
+
15
+ ```ts title="src/consent-scripts.ts"
16
+ import { attentiveTag } from '@c15t/integrations/attentive';
17
+
18
+ export const scripts = [attentiveTag({ siteSlug: 'YOUR_SITE' })];
19
+ ```
20
+
21
+ | Option | Type | Required | Description |
22
+ | ----------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
23
+ | `siteSlug` | `string` | Yes | Site slug from your Attentive tag URL, `https://cdn.attn.tv/<siteSlug>/dtag.js`. Ask your Attentive contact if you don't have it. |
24
+ | `scriptUrl` | `string` | No | Exact tag URL from Attentive or your proxy. |
25
+
26
+ **When consent is withdrawn:** c15t reloads the page, and the next page does not load the tag. Attentive has no supported way to stop a running tag, so keep `reloadOnConsentRevoked` on.
27
+
28
+ * Some Attentive identifiers, such as `__attentive_id`, last up to 390 days. The reload does not delete them.
29
+
30
+ ## Customer.io
31
+
32
+ Customer.io collects events and visitor identity on your site through its Data Pipelines JavaScript source, for use in its messaging. Waits for `marketing` consent.
33
+
34
+ ```ts title="src/customer-io.ts"
35
+ import { customerIo } from '@c15t/integrations/customer-io';
36
+
37
+ export const scripts = [customerIo({ writeKey: 'YOUR_WRITE_KEY' })];
38
+ ```
39
+
40
+ | Option | Type | Required | Description |
41
+ | ------------- | ------------------------- | -------- | ----------------------------------------------------------------- |
42
+ | `writeKey` | `string` | Yes | Write key of your Data Pipelines JavaScript source. |
43
+ | `region` | `'us'` or `'eu'` | No | Region of the source. Defaults to `us`. |
44
+ | `loadOptions` | `Record<string, unknown>` | No | Options for the Customer.io loader, such as destination settings. |
45
+ | `cdnUrl` | `string` | No | Origin of a proxy for the loader, source settings and SDK files. |
46
+
47
+ **When consent is withdrawn:** the helper calls `cioanalytics.reset()` to clear the stored identity, and c15t reloads the page. `reset()` is not an opt-out. It does not stop the SDK or destination SDKs from sending, so keep `reloadOnConsentRevoked` on.
48
+
49
+ * The helper sends the first `page()` call itself. It always sets `initialPageview` to `false`, even if you pass `true`.
50
+ * `cdnUrl` moves only the loader, settings and SDK files to your proxy. To proxy collected events too, set `apiHost` for the `Customer.io Data Pipelines` integration in `loadOptions`:
51
+
52
+ ```ts title="src/customer-io-proxy.ts"
53
+ import { customerIo } from '@c15t/integrations/customer-io';
54
+
55
+ export const proxiedScripts = [
56
+ customerIo({
57
+ cdnUrl: 'https://analytics.example.com',
58
+ loadOptions: {
59
+ integrations: {
60
+ 'Customer.io Data Pipelines': {
61
+ apiHost: 'collect.example.com/v1',
62
+ protocol: 'https',
63
+ },
64
+ },
65
+ },
66
+ writeKey: 'YOUR_WRITE_KEY',
67
+ }),
68
+ ];
69
+ ```
70
+
71
+ ## Insider One
72
+
73
+ Insider One runs personalized web campaigns and builds customer profiles from on-site behavior. Waits for `marketing` consent.
74
+
75
+ ```ts title="src/consent-scripts.ts"
76
+ import { insiderTag } from '@c15t/integrations/insider';
77
+
78
+ export const scripts = [
79
+ insiderTag({
80
+ currency: 'GBP',
81
+ pageType: {
82
+ type: 'category',
83
+ value: { breadcrumb: ['Clothing', 'Dresses'] },
84
+ },
85
+ partnerId: 'YOUR_PARTNER_ID',
86
+ partnerName: 'yourpartner',
87
+ }),
88
+ ];
89
+ ```
90
+
91
+ | Option | Type | Required | Description |
92
+ | ------------- | ------------------------- | -------- | ------------------------------------------------------------------------------------------------- |
93
+ | `partnerName` | `string` | Yes | Public partner name from Insider. |
94
+ | `partnerId` | `string` | Yes | Public partner ID from Insider. |
95
+ | `user` | `Record<string, unknown>` | No | Current user attributes, such as `email`, `phone_number` and `custom`. Must be JSON-serializable. |
96
+ | `currency` | `string` | No | Current currency, such as `'GBP'`. Set it on multi-currency sites. |
97
+ | `pageType` | `InsiderPageType` | No | Page definition from Insider's Web SDK guide. Defaults to `{ type: 'other' }`. |
98
+
99
+ **When consent is withdrawn:** the helper pushes `gdpr_optin: false`, a neutral `other` page and `init`, then c15t reloads the page. Insider has no unload method, so keep `reloadOnConsentRevoked` on.
100
+
101
+ * The helper sets `gdpr_optin` from consent and overrides any value in `user`. If you push `user` data to `InsiderQueue` yourself, set `gdpr_optin` too, because Insider treats a missing value as `true`.
102
+ * The helper does not deduplicate purchases. Set `pageType: { type: 'purchase', ... }` only when your backend claims that order ID for the first time. Use `{ type: 'other' }` when the confirmation page reloads or opens again.
103
+
104
+ ## Marketo Munchkin
105
+
106
+ Munchkin is Adobe Marketo's script for tracking known and anonymous leads on your site. Waits for `marketing` consent.
107
+
108
+ ```ts title="src/marketo.ts"
109
+ import { marketoMunchkin } from '@c15t/integrations/marketo';
110
+
111
+ export const scripts = [marketoMunchkin({ munchkinId: 'AAA-BBB-CCC' })];
112
+ ```
113
+
114
+ | Option | Type | Required | Description |
115
+ | ------------- | ------------------------- | -------- | --------------------------------------------------------------------- |
116
+ | `munchkinId` | `string` | Yes | Account ID from Marketo's installation snippet. |
117
+ | `initOptions` | `Record<string, unknown>` | No | Passed to `Munchkin.init`, for example `cookieAnon` or `anonymizeIP`. |
118
+ | `category` | consent condition | No | Replaces the default `marketing` condition. |
119
+ | `scriptUrl` | `string` | No | Custom or proxied loader URL. |
120
+
121
+ **When consent is withdrawn:** c15t removes the loader and reloads the page. Munchkin has no API to stop tracking, so keep `reloadOnConsentRevoked` on.
122
+
123
+ * `cookieAnon: false` stops Munchkin from creating cookies for new anonymous visitors. It is not a consent switch: visitors who already have a cookie are still tracked.
124
+ * The `_mkto_trk` cookie lasts two years by default and survives the reload. The next page does not load Munchkin while consent is denied.
125
+
126
+ ## Salesforce Account Engagement
127
+
128
+ Account Engagement, formerly Pardot, tracks visitors and prospects for Salesforce B2B marketing. Waits for `marketing` consent.
129
+
130
+ ```ts title="src/consent-scripts.ts"
131
+ import { pardotTracking } from '@c15t/integrations/salesforce-pardot';
132
+
133
+ export const scripts = [
134
+ pardotTracking({
135
+ piAId: '123456',
136
+ piCId: '654321',
137
+ piDomain: 'pi.pardot.com',
138
+ }),
139
+ ];
140
+ ```
141
+
142
+ Replace `pi.pardot.com` in the example with your verified tracker domain.
143
+
144
+ | Option | Type | Required | Description |
145
+ | ------------ | -------------------------- | --------------- | ----------------------------------------------------------------------------------------------------- |
146
+ | `piAId` | `string` or `number` | Yes | Account ID from the tracking snippet. |
147
+ | `piCId` | `string` or `number` | Yes | Campaign ID from the tracking snippet. |
148
+ | `piDomain` | `string` | In consent mode | Verified tracker domain, without a scheme or path. |
149
+ | `mode` | `'consent'` or `'classic'` | No | `consent`, the default, uses the Tracking and Consent API. `classic` loads the older `pd.js` snippet. |
150
+ | `piHostname` | `string` | No | Tracker hostname in classic mode. Defaults to `pi.pardot.com`. |
151
+
152
+ **When consent is withdrawn:** in consent mode, the helper calls `pdt('revokeConsent')`. Classic mode has no revoke call. Keep `reloadOnConsentRevoked` on in both modes, because the reload is what stops the running tracker.
153
+
154
+ * Turn on the Tracking and Consent API in Account Engagement before you use consent mode.
155
+ * Copy `piAId` and `piCId` from the generated snippet as they are. They already include Salesforce's +1000 offset, and the helper does not add it again.
156
+ * Do not call `clearTrackingCookies` after `revokeConsent`. It also deletes the cookie that records the refusal, so later page views can be tracked.
@@ -0,0 +1,222 @@
1
+ ---
2
+ title: Experimentation and personalization tools
3
+ description: Consent-gated helpers for experimentation and personalization tools
4
+ that don't need a full guide.
5
+ icon: flask-conical
6
+ group: integrations
7
+ ---
8
+
9
+ Each helper below loads its vendor only after the visitor allows the listed consent category. For setup, see [Integrations overview](./overview.md).
10
+
11
+ ## Adobe Target
12
+
13
+ Adobe Target runs A/B tests and personalizes page content. Waits for
14
+ `experience` consent. Pick one mode per page: your self-hosted at.js file, or
15
+ Adobe Experience Platform Web SDK.
16
+
17
+ ```ts title="src/consent-scripts.ts"
18
+ import { adobeTarget } from '@c15t/integrations/adobe-target';
19
+
20
+ export const scripts = [
21
+ adobeTarget({
22
+ mode: 'atjs',
23
+ scriptUrl: '/scripts/at.js',
24
+ targetGlobalSettings: { secureOnly: true },
25
+ }),
26
+ ];
27
+ ```
28
+
29
+ ```ts title="src/consent-scripts.ts"
30
+ import { adobeTarget } from '@c15t/integrations/adobe-target';
31
+
32
+ export const scripts = [
33
+ adobeTarget({
34
+ datastreamId: 'YOUR_DATASTREAM_ID',
35
+ mode: 'websdk',
36
+ orgId: 'YOUR_ORG_ID@AdobeOrg',
37
+ version: '2.30.0',
38
+ }),
39
+ ];
40
+ ```
41
+
42
+ | Option | Type | Required | Description |
43
+ | ---------------------- | ------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
44
+ | `mode` | `'atjs' \|'websdk'` | Yes | Which Adobe SDK to load. |
45
+ | `scriptUrl` | `string` | at.js | URL of your self-hosted at.js file. |
46
+ | `targetGlobalSettings` | `object` | No | at.js settings such as `cookieDomain`, `secureOnly`, `bodyHidingEnabled` and `bodyHiddenStyle`. The helper always sets `optinEnabled: true`. |
47
+ | `datastreamId` | `string` | Web SDK | ID of a Target-enabled datastream. |
48
+ | `orgId` | `string` | Web SDK | Organization ID, including `@AdobeOrg`. |
49
+ | `version` | `string` | Web SDK | Exact Web SDK version, `2.21.0` or later. |
50
+ | `category` | `HasCondition` | No | Consent condition to wait for. Defaults to `'experience'`. |
51
+
52
+ **When consent is withdrawn:** with at.js, the helper calls
53
+ `adobe.optIn.deny(adobe.OptInCategories.TARGET)` if the Opt-in service is on
54
+ the page. With Web SDK, it sends `setConsent` with `'out'`. Neither undoes
55
+ content Target already changed, so keep `reloadOnConsentRevoked` on.
56
+
57
+ Remove Adobe's prehiding snippet when you use this helper. Without it, visitors
58
+ see the original page first, then Target's changes once consent allows the
59
+ SDK to load.
60
+
61
+ * at.js has no Adobe CDN, so host the file yourself. Its opt-in API comes from
62
+ the Experience Cloud ID (ECID) Opt-in service, which the helper doesn't load.
63
+ Without it, only the page reload stops at.js.
64
+ * Web SDK needs version 2.21 or later. The helper configures the SDK and sends
65
+ consent but never calls `sendEvent`. Gate your own `sendEvent` calls behind
66
+ the same consent.
67
+ * Web SDK can be configured only once per page. Reload the page before you
68
+ mount the helper again or change its IDs, version or mode.
69
+
70
+ ## Appcues
71
+
72
+ Appcues shows onboarding flows, tooltips and checklists inside your app. Waits
73
+ for `experience` consent.
74
+
75
+ ```ts title="src/consent-scripts.ts"
76
+ import { appcues } from '@c15t/integrations/appcues';
77
+
78
+ export const scripts = [
79
+ appcues({
80
+ accountId: 'YOUR_APPCUES_ID',
81
+ onReady: () => {
82
+ // Read the signed-in visitor from your auth state here.
83
+ // Enable only with their real, unique, stable ID; skip if signed out.
84
+ // window.Appcues?.identify?.('CURRENT_USER_ID');
85
+ // After identify, enable with the visitor's real group ID if needed.
86
+ // window.Appcues?.group?.('CURRENT_GROUP_ID');
87
+ // After identify, track only an event that actually happened.
88
+ // window.Appcues?.track?.('Started onboarding');
89
+ },
90
+ }),
91
+ ];
92
+ ```
93
+
94
+ | Option | Type | Required | Description |
95
+ | ----------- | ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
96
+ | `accountId` | `string` | Yes | Account ID from your [Appcues install snippet](https://docs.appcues.com/install-appcues-web/install-the-appcues-sdk). A blank ID logs an error and skips loading. |
97
+ | `region` | `'us' \|'eu'` | No | Account region. Defaults to `'us'`. |
98
+ | `settings` | `object` | No | `AppcuesSettings` to set before the SDK loads. Defaults to `{ enableURLDetection: true }`. |
99
+ | `onReady` | `() => void` | No | Runs once after the SDK loads, while consent still allows it. Call `identify`, `group` and `track` here. |
100
+
101
+ **When consent is withdrawn:** the helper calls `Appcues.reset()` and removes
102
+ the loader, then c15t reloads the page. Keep `reloadOnConsentRevoked` on.
103
+ `reset()` clears the user and any flow in progress, but the SDK keeps running
104
+ until the reload.
105
+
106
+ * Set `region: 'eu'` for an EU account. The SDK then loads from
107
+ `fast.eu.appcues.com` instead of `fast.appcues.com`.
108
+ * Call `identify` from `onReady`, using the signed-in user's real, stable ID.
109
+ If the user signs in later, call `identify` from your auth handler, but first
110
+ check that `window.Appcues` exists and consent still allows Appcues.
111
+
112
+ ## Dynamic Yield
113
+
114
+ Dynamic Yield personalizes pages and serves product recommendations. Waits for
115
+ `experience` consent.
116
+
117
+ ```ts title="src/consent-scripts.ts"
118
+ import { dynamicYield } from '@c15t/integrations/dynamic-yield';
119
+
120
+ export const scripts = [
121
+ dynamicYield({
122
+ recommendationContext: { lng: 'en_GB', type: 'HOMEPAGE' },
123
+ region: 'eu',
124
+ sectionId: 'YOUR_SECTION_ID',
125
+ }),
126
+ ];
127
+ ```
128
+
129
+ | Option | Type | Required | Description |
130
+ | ----------------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
131
+ | `sectionId` | `string` | Yes | Section ID from your [installation snippet](https://dy.dev/docs/implement-script). A blank ID logs an error and skips loading. |
132
+ | `region` | `'us' \|'eu'` | No | Data center for the section. Defaults to `'us'`. |
133
+ | `recommendationContext` | `object` | No | [Page context](https://dy.dev/docs/page-context) set before the scripts load, such as `{ type: 'PRODUCT', data: ['SKU-1'] }`. Your app updates it on client-side navigation. |
134
+ | `category` | `HasCondition` | No | Consent condition to wait for. Defaults to `'experience'`. |
135
+
136
+ After consent, the helper sets `DY.userActiveConsent = { accepted: true }`,
137
+ loads `api_dynamic.js`, then loads `api_static.js`.
138
+
139
+ **When consent is withdrawn:** the helper removes both scripts and sets
140
+ `DY.userActiveConsent = { accepted: false }`. The SDK that already ran stays
141
+ loaded, so keep `reloadOnConsentRevoked` on.
142
+
143
+ * Ask Dynamic Yield Support to enable
144
+ [Active Cookie Consent](https://dy.dev/docs/active-cookie-consent) for your
145
+ section. Until they do, Dynamic Yield ignores the consent signal.
146
+ * Use one section and region per page. Dynamic Yield keeps its consent and page
147
+ context on the page-wide `window.DY`.
148
+ * Expect flicker. Dynamic Yield wants synchronous scripts in `<head>`. Loading
149
+ after consent, one script after the other, can show the original page before
150
+ a campaign applies.
151
+
152
+ ## Optimizely Web Experimentation
153
+
154
+ Optimizely Web runs A/B tests and personalization by changing the page in the
155
+ browser. Waits for `experience` consent.
156
+
157
+ ```ts title="src/consent-scripts.ts"
158
+ import { optimizelyWeb } from '@c15t/integrations/optimizely';
159
+
160
+ export const scripts = [optimizelyWeb({ projectId: '12345678' })];
161
+ ```
162
+
163
+ | Option | Type | Required | Description |
164
+ | ----------- | ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
165
+ | `projectId` | `string \|number` | Yes | The number before `.js` in your snippet URL, from **Settings > Implementation**. Invalid IDs log an error and skip loading. |
166
+ | `category` | `HasCondition` | No | Consent condition to wait for. Defaults to `'experience'`. |
167
+ | `scriptSrc` | `string` | No | Custom or proxied snippet URL. Defaults to `https://cdn.optimizely.com/js/<projectId>.js`. |
168
+
169
+ **When consent is withdrawn:** c15t removes the snippet and reloads the page.
170
+ Keep `reloadOnConsentRevoked` on. The running SDK can't be unloaded, so
171
+ without the reload it keeps changing the page and sending events.
172
+
173
+ * Expect flicker. Optimizely wants a parser-blocking snippet at the top of
174
+ `<head>`. c15t injects it with `async: false` after consent, which doesn't
175
+ block the parser, so visitors can see the original page before a variation
176
+ applies.
177
+ * `optOutOptimizely()` from `@c15t/integrations/optimizely` sets Optimizely's
178
+ `optimizelyOptOut` cookie for ten years. Granting consent later in c15t
179
+ doesn't clear it, so the helper never calls it on revoke. Call it only for a
180
+ deliberate permanent opt-out, and clear the cookie yourself to undo it.
181
+
182
+ ## Wisepops
183
+
184
+ Wisepops shows popups, bars, embeds and notification feeds on your site. Waits
185
+ for `marketing` consent. The `experienceScripts` export below shows the
186
+ `experience` alternative; register one of the two arrays.
187
+
188
+ ```ts title="src/consent-scripts.ts"
189
+ import { wisepops } from '@c15t/integrations/wisepops';
190
+
191
+ export const scripts = [wisepops({ siteId: 'YOUR_WISEPOPS_SITE_ID' })];
192
+
193
+ export const experienceScripts = [
194
+ wisepops({
195
+ category: 'experience',
196
+ options: { browserStorage: true },
197
+ siteId: 'YOUR_WISEPOPS_SITE_ID',
198
+ }),
199
+ ];
200
+ ```
201
+
202
+ | Option | Type | Required | Description |
203
+ | ------------------------ | ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------- |
204
+ | `siteId` | `string` | Yes | The `h` value in the loader URL from **Settings > Install Wisepops**. A blank ID logs an error and skips loading. |
205
+ | `category` | `HasCondition` | No | Consent condition to wait for. Defaults to `'marketing'`. Use `'experience'` if that fits your campaigns. |
206
+ | `options.autoPageview` | `'url' \|'init' \|false` | No | When Wisepops checks for campaigns. Defaults to `'url'`, which follows client-side navigation. |
207
+ | `options.browserStorage` | `boolean` | No | `true` stores visitor data in localStorage instead of cookies. |
208
+
209
+ The helper queues `defaultConsent: 'denied'`, loads Wisepops, then calls
210
+ `wisepops('consent', 'granted')`.
211
+
212
+ **When consent is withdrawn:** c15t removes the loader and reloads the page.
213
+ Keep `reloadOnConsentRevoked` on. Wisepops has no way to revoke consent or shut
214
+ down from code, so the reload is the only way to stop it.
215
+
216
+ * Wisepops' own `denied` mode still shows campaigns, with session-only
217
+ cookies. This helper doesn't use it. It keeps Wisepops off the page until
218
+ consent, so don't install Wisepops a second time to get denied mode.
219
+ * Wisepops integrates with Shopify Customer Privacy on its own, and this helper
220
+ doesn't sync c15t choices to Shopify. On a Shopify store, check Wisepops'
221
+ [consent mode](https://support.wisepops.com/en/articles/12679556-cookie-consent-mode)
222
+ settings and make sure a Shopify app doesn't load Wisepops outside c15t.
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  title: Integrations
3
3
  description: Find all c15t integrations for analytics, tag managers,
4
- advertising, email and SMS, chat and embedded content.
4
+ advertising, experimentation and personalization, email and SMS, chat and
5
+ embedded content.
5
6
  seoTitle: Cookie consent integrations for analytics and ads | c15t
6
7
  seoDescription: Connect analytics, ad pixels, chat widgets and embeds to your
7
8
  cookie banner. c15t has helpers for Google Tag Manager, Meta Pixel, PostHog,
@@ -53,11 +54,15 @@ kernel to control its DOM lifecycle. Embeds do not require `@c15t/integrations`.
53
54
 
54
55
  | Integration | Helper | Category | Loading behavior |
55
56
  | --------------------------------------------------------- | ------------------------ | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
57
+ | [Lucky Orange](./other-analytics.md#lucky-orange) | `luckyOrange` | `measurement` | Waits for effective permission |
56
58
  | [Google Tag](./google-tag.md) | `gtag` | `measurement` or `marketing` | Always loads and signals Google consent by default; `loadMode: 'after-consent'` waits for permission |
59
+ | [Pendo](./other-analytics.md#pendo) | `pendo` | `measurement` | Waits for effective permission; stops and resumes event sending |
57
60
  | [Ahrefs Analytics](./ahrefs-analytics.md) | `ahrefsAnalytics` | `measurement` | Waits for effective permission |
58
61
  | [Adobe Analytics](./adobe-analytics.md) | `adobeAnalytics` | `measurement` | Waits for effective permission |
59
62
  | [Amplitude](./amplitude.md) | `amplitude` | `measurement` | Waits for effective permission; opts the SDK out on revocation |
60
63
  | [Cloudflare Web Analytics](./cloudflare-web-analytics.md) | `cloudflareWebAnalytics` | `measurement` | Waits for effective permission |
64
+ | [RB2B](./other-analytics.md#rb2b) | `rb2b` | `marketing` | Waits for effective permission |
65
+ | [Clay](./clay.md) | `clay` | `marketing` | Waits for effective permission |
61
66
  | [Clearbit](./clearbit.md) | `clearbit` | `marketing` | Waits for effective permission |
62
67
  | [Microsoft Clarity](./microsoft-clarity.md) | `clarity` | `measurement` | Gated initially; keeps the SDK and signals storage consent |
63
68
  | [Databuddy](./databuddy.md) | `databuddy` | `measurement` | Always loads; switches the SDK's disabled flag |
@@ -77,37 +82,66 @@ kernel to control its DOM lifecycle. Embeds do not require `@c15t/integrations`.
77
82
  | [Segment](./segment.md) | `segment` | `measurement` | Waits for effective permission |
78
83
  | [Sentry](./sentry.md) | `sentry` | `necessary`; Replay and user data `measurement` | Configurable; defaults to always loading and loads Replay after permission |
79
84
  | [Rybbit Analytics](./rybbit-analytics.md) | `rybbitAnalytics` | `measurement` | Waits for effective permission |
85
+ | [Statable](./other-analytics.md#statable) | `statableAnalytics` | `measurement` | Waits for effective permission |
80
86
  | [Umami Analytics](./umami-analytics.md) | `umamiAnalytics` | `measurement` | Waits for effective permission |
81
87
  | [Vercel Analytics](./vercel-analytics.md) | `vercelAnalytics` | `measurement` | Waits for effective permission |
82
88
  | [Vercel Speed Insights](./vercel-speed-insights.md) | `vercelSpeedInsights` | `measurement` | Waits for effective permission; drops reports after revocation |
83
89
 
90
+ ## Experimentation and personalization
91
+
92
+ | Integration | Helper | Category | Loading behavior |
93
+ | -------------------------------------------------------------------------------------- | --------------- | ------------ | ------------------------------ |
94
+ | [Adobe Target](./other-experience.md#adobe-target) | `adobeTarget` | `experience` | Waits for effective permission |
95
+ | [Appcues](./other-experience.md#appcues) | `appcues` | `experience` | Waits for effective permission |
96
+ | [Dynamic Yield](./other-experience.md#dynamic-yield) | `dynamicYield` | `experience` | Waits for effective permission |
97
+ | [Optimizely Web Experimentation](./other-experience.md#optimizely-web-experimentation) | `optimizelyWeb` | `experience` | Waits for effective permission |
98
+ | [Wisepops](./other-experience.md#wisepops) | `wisepops` | `marketing` | Waits for effective permission |
99
+
84
100
  ## Chat and support
85
101
 
86
- | Integration | Helper | Category | Loading behavior |
87
- | ----------------------------- | ----------- | --------------- | ------------------------------ |
88
- | [Crisp](./crisp.md) | `crisp` | `functionality` | Waits for effective permission |
89
- | [Front Chat](./front-chat.md) | `frontChat` | `functionality` | Waits for effective permission |
90
- | [Intercom](./intercom.md) | `intercom` | `functionality` | Waits for effective permission |
102
+ | Integration | Helper | Category | Loading behavior |
103
+ | ------------------------------------------------------------------ | --------------- | --------------- | ------------------------------ |
104
+ | [Ada](./other-chat-and-support.md#ada) | `ada` | `functionality` | Waits for effective permission |
105
+ | [Crisp](./crisp.md) | `crisp` | `functionality` | Waits for effective permission |
106
+ | [Front Chat](./front-chat.md) | `frontChat` | `functionality` | Waits for effective permission |
107
+ | [Gorgias Chat](./other-chat-and-support.md#gorgias-chat) | `gorgiasChat` | `functionality` | Waits for effective permission |
108
+ | [Intercom](./intercom.md) | `intercom` | `functionality` | Waits for effective permission |
109
+ | [Zendesk Messaging](./other-chat-and-support.md#zendesk-messaging) | `zendeskWidget` | `functionality` | Waits for effective permission |
91
110
 
92
111
  ## Email and SMS
93
112
 
94
- | Integration | Helper | Category | Loading behavior |
95
- | ----------------------- | --------- | ----------------------------- | ---------------------------------------------------------- |
96
- | [Klaviyo](./klaviyo.md) | `klaviyo` | `marketing` and `measurement` | Waits for both; optional forms-only mode needs `marketing` |
113
+ | Integration | Helper | Category | Loading behavior |
114
+ | --------------------------------------------------------------------------------------- | ----------------- | ----------------------------- | ---------------------------------------------------------- |
115
+ | [Attentive](./other-email-and-sms.md#attentive) | `attentiveTag` | `marketing` | Waits for effective permission |
116
+ | [Customer.io](./other-email-and-sms.md#customerio) | `customerIo` | `marketing` | Waits for effective permission |
117
+ | [Insider](./other-email-and-sms.md#insider-one) | `insiderTag` | `marketing` | Waits for effective permission |
118
+ | [Klaviyo](./klaviyo.md) | `klaviyo` | `marketing` and `measurement` | Waits for both; optional forms-only mode needs `marketing` |
119
+ | [Marketo Munchkin](./other-email-and-sms.md#marketo-munchkin) | `marketoMunchkin` | `marketing` | Waits for effective permission |
120
+ | [Salesforce Account Engagement](./other-email-and-sms.md#salesforce-account-engagement) | `pardotTracking` | `marketing` | Waits for effective permission |
97
121
 
98
122
  ## Ads and pixels
99
123
 
100
- | Integration | Helper | Category | Loading behavior |
101
- | ---------------------------------------------- | ------------------ | ----------- | ---------------------------------------------------------------- |
102
- | [Meta Pixel](./meta-pixel.md) | `metaPixel` | `marketing` | Gated initially; keeps the SDK and signals consent |
103
- | [OpenAI Pixel](./openai-pixel.md) | `openaiPixel` | `marketing` | Gated initially; keeps the SDK and signals consent |
104
- | [Pinterest Tag](./pinterest-tag.md) | `pinterestTag` | `marketing` | Gated initially; keeps the tag and signals consent |
105
- | [Reddit Pixel](./reddit-pixel.md) | `redditPixel` | `marketing` | Gated initially; keeps the pixel and toggles first-party cookies |
106
- | [TikTok Pixel](./tiktok-pixel.md) | `tiktokPixel` | `marketing` | Gated initially; keeps the SDK and signals consent |
107
- | [LinkedIn Insight Tag](./linkedin-insights.md) | `linkedinInsights` | `marketing` | Waits for effective permission |
108
- | [Microsoft UET](./microsoft-uet.md) | `microsoftUet` | `marketing` | Always loads; signals ad storage consent |
109
- | [Snapchat Pixel](./snapchat-pixel.md) | `snapchatPixel` | `marketing` | Waits for effective permission |
110
- | [X Pixel](./x-pixel.md) | `xPixel` | `marketing` | Waits for effective permission |
124
+ | Integration | Helper | Category | Loading behavior |
125
+ | ---------------------------------------------------------- | ------------------- | ----------- | ---------------------------------------------------------------- |
126
+ | [The Trade Desk](./other-ads-and-pixels.md#the-trade-desk) | `theTradeDeskPixel` | `marketing` | Waits for effective permission |
127
+ | [LiveIntent](./other-ads-and-pixels.md#liveintent) | `liveIntentTag` | `marketing` | Waits for effective permission |
128
+ | [AdRoll](./other-ads-and-pixels.md#adroll) | `adrollPixel` | `marketing` | Waits for effective permission |
129
+ | [Vector](./other-ads-and-pixels.md#vector) | `vector` | `marketing` | Waits for effective permission |
130
+ | [Meta Pixel](./meta-pixel.md) | `metaPixel` | `marketing` | Gated initially; keeps the SDK and signals consent |
131
+ | [OpenAI Pixel](./openai-pixel.md) | `openaiPixel` | `marketing` | Gated initially; keeps the SDK and signals consent |
132
+ | [Pinterest Tag](./pinterest-tag.md) | `pinterestTag` | `marketing` | Gated initially; keeps the tag and signals consent |
133
+ | [Reddit Pixel](./reddit-pixel.md) | `redditPixel` | `marketing` | Gated initially; keeps the pixel and toggles first-party cookies |
134
+ | [TikTok Pixel](./tiktok-pixel.md) | `tiktokPixel` | `marketing` | Gated initially; keeps the SDK and signals consent |
135
+ | [LinkedIn Insight Tag](./linkedin-insights.md) | `linkedinInsights` | `marketing` | Waits for effective permission |
136
+ | [Microsoft UET](./microsoft-uet.md) | `microsoftUet` | `marketing` | Always loads; signals ad storage consent |
137
+ | [Snapchat Pixel](./snapchat-pixel.md) | `snapchatPixel` | `marketing` | Waits for effective permission |
138
+ | [X Pixel](./x-pixel.md) | `xPixel` | `marketing` | Waits for effective permission |
139
+
140
+ ## Commerce
141
+
142
+ | Integration | Helper | Category | Loading behavior |
143
+ | ----------------------- | ------------------------------ | ------------------------------------------- | ------------------------------------------------------------------ |
144
+ | [Shopify](./shopify.md) | `createShopifyCustomerPrivacy` | `measurement`, `marketing`, `functionality` | Loads nothing; sends each choice to Shopify's Customer Privacy API |
111
145
 
112
146
  ## Load each vendor once
113
147
 
@@ -166,7 +200,7 @@ the scripts or the consent snapshot yourself:
166
200
  | ------------------------------ | ------------------------------------------------------------------------------ |
167
201
  | React, Next.js, TanStack Start | `useEventDispatcher()` from `c15t/react`, `c15t/next` or `c15t/tanstack-start` |
168
202
  | Vue, Nuxt | `useEventDispatcher()` from `c15t/vue/vue-plugin`, auto-imported in Nuxt |
169
- | Svelte, SvelteKit | `getEventDispatcher()` from `@c15t/svelte`, called inside `ConsentProvider` |
203
+ | Svelte, SvelteKit | `getEventDispatcher()` from `c15t/svelte`, called inside `ConsentProvider` |
170
204
  | Astro | `getEventDispatcher()` from `c15t/astro/client` |
171
205
  | JavaScript | `getEventDispatcher(runtime)` from `@c15t/browser` |
172
206
 
@@ -166,7 +166,7 @@ pass them as a top-level prop:
166
166
 
167
167
  ```svelte title="src/App.svelte"
168
168
  <script lang="ts">
169
- import { ConsentProvider, manifest } from '@c15t/svelte';
169
+ import { ConsentProvider, manifest } from 'c15t/svelte';
170
170
  import { scripts } from './consent-scripts';
171
171
  </script>
172
172
 
@@ -185,7 +185,7 @@ handle and the layout load from the [SvelteKit quickstart](https://c15t.com/docs
185
185
 
186
186
  ```svelte title="src/routes/+layout.svelte"
187
187
  <script lang="ts">
188
- import { ConsentRoot } from '@c15t/svelte';
188
+ import { ConsentRoot } from 'c15t/svelte';
189
189
  import { scripts } from '../consent-scripts';
190
190
 
191
191
  let { children, data } = $props();
@@ -165,7 +165,7 @@ pass them as a top-level prop:
165
165
 
166
166
  ```svelte title="src/App.svelte"
167
167
  <script lang="ts">
168
- import { ConsentProvider, manifest } from '@c15t/svelte';
168
+ import { ConsentProvider, manifest } from 'c15t/svelte';
169
169
  import { scripts } from './consent-scripts';
170
170
  </script>
171
171
 
@@ -184,7 +184,7 @@ handle and the layout load from the [SvelteKit quickstart](https://c15t.com/docs
184
184
 
185
185
  ```svelte title="src/routes/+layout.svelte"
186
186
  <script lang="ts">
187
- import { ConsentRoot } from '@c15t/svelte';
187
+ import { ConsentRoot } from 'c15t/svelte';
188
188
  import { scripts } from '../consent-scripts';
189
189
 
190
190
  let { children, data } = $props();
@@ -165,7 +165,7 @@ pass them as a top-level prop:
165
165
 
166
166
  ```svelte title="src/App.svelte"
167
167
  <script lang="ts">
168
- import { ConsentProvider, manifest } from '@c15t/svelte';
168
+ import { ConsentProvider, manifest } from 'c15t/svelte';
169
169
  import { scripts } from './consent-scripts';
170
170
  </script>
171
171
 
@@ -184,7 +184,7 @@ handle and the layout load from the [SvelteKit quickstart](https://c15t.com/docs
184
184
 
185
185
  ```svelte title="src/routes/+layout.svelte"
186
186
  <script lang="ts">
187
- import { ConsentRoot } from '@c15t/svelte';
187
+ import { ConsentRoot } from 'c15t/svelte';
188
188
  import { scripts } from '../consent-scripts';
189
189
 
190
190
  let { children, data } = $props();
@@ -178,7 +178,7 @@ pass them as a top-level prop:
178
178
 
179
179
  ```svelte title="src/App.svelte"
180
180
  <script lang="ts">
181
- import { ConsentProvider, manifest } from '@c15t/svelte';
181
+ import { ConsentProvider, manifest } from 'c15t/svelte';
182
182
  import { scripts } from './consent-scripts';
183
183
  </script>
184
184
 
@@ -197,7 +197,7 @@ handle and the layout load from the [SvelteKit quickstart](https://c15t.com/docs
197
197
 
198
198
  ```svelte title="src/routes/+layout.svelte"
199
199
  <script lang="ts">
200
- import { ConsentRoot } from '@c15t/svelte';
200
+ import { ConsentRoot } from 'c15t/svelte';
201
201
  import { scripts } from '../consent-scripts';
202
202
 
203
203
  let { children, data } = $props();
@@ -163,7 +163,7 @@ pass them as a top-level prop:
163
163
 
164
164
  ```svelte title="src/App.svelte"
165
165
  <script lang="ts">
166
- import { ConsentProvider, manifest } from '@c15t/svelte';
166
+ import { ConsentProvider, manifest } from 'c15t/svelte';
167
167
  import { scripts } from './consent-scripts';
168
168
  </script>
169
169
 
@@ -182,7 +182,7 @@ handle and the layout load from the [SvelteKit quickstart](https://c15t.com/docs
182
182
 
183
183
  ```svelte title="src/routes/+layout.svelte"
184
184
  <script lang="ts">
185
- import { ConsentRoot } from '@c15t/svelte';
185
+ import { ConsentRoot } from 'c15t/svelte';
186
186
  import { scripts } from '../consent-scripts';
187
187
 
188
188
  let { children, data } = $props();
@@ -165,7 +165,7 @@ pass them as a top-level prop:
165
165
 
166
166
  ```svelte title="src/App.svelte"
167
167
  <script lang="ts">
168
- import { ConsentProvider, manifest } from '@c15t/svelte';
168
+ import { ConsentProvider, manifest } from 'c15t/svelte';
169
169
  import { scripts } from './consent-scripts';
170
170
  </script>
171
171
 
@@ -184,7 +184,7 @@ handle and the layout load from the [SvelteKit quickstart](https://c15t.com/docs
184
184
 
185
185
  ```svelte title="src/routes/+layout.svelte"
186
186
  <script lang="ts">
187
- import { ConsentRoot } from '@c15t/svelte';
187
+ import { ConsentRoot } from 'c15t/svelte';
188
188
  import { scripts } from '../consent-scripts';
189
189
 
190
190
  let { children, data } = $props();