@c15t/react 3.0.0-alpha.4 → 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.
Files changed (111) hide show
  1. package/AGENTS.md +8 -5
  2. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  3. package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
  4. package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
  5. package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
  6. package/dist/components/iab-panel/index.js +1 -1
  7. package/dist/components/panel/index.js +1 -1
  8. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  9. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -1
  10. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  11. package/dist/components/preferences/atoms/accordion.js +1 -1
  12. package/dist/components/preferences/index.js +1 -1
  13. package/dist/components/shared/primitives/button.js +1 -1
  14. package/dist/components/shared/ui/accordion/accordion.js +1 -1
  15. package/dist/components/shared/ui/collapsible/collapsible.js +1 -1
  16. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  17. package/dist/components/shared/ui/switch/switch.js +1 -1
  18. package/dist/components/shared/ui/tabs/tabs.js +1 -1
  19. package/dist/devtools-launcher.js +2 -0
  20. package/dist/devtools.js +1 -1
  21. package/dist/draft-context.js +2 -0
  22. package/dist/draft.js +1 -1
  23. package/dist/external-iab-context.js +1 -1
  24. package/dist/gpp.js +2 -0
  25. package/dist/hooks/use-color-scheme.js +1 -1
  26. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  27. package/dist/hooks.js +1 -1
  28. package/dist/index-parts/consent-banner.js +1 -0
  29. package/dist/index-parts/consent-dialog-link.js +1 -0
  30. package/dist/index-parts/consent-dialog-trigger.js +1 -0
  31. package/dist/index-parts/consent-dialog.js +1 -0
  32. package/dist/index-parts/consent-gate.js +1 -0
  33. package/dist/index-parts/core.js +1 -0
  34. package/dist/index-parts/draft.js +1 -0
  35. package/dist/index-parts/hooks.js +1 -0
  36. package/dist/index-parts/module-hooks.js +1 -0
  37. package/dist/index-parts/provider.js +1 -0
  38. package/dist/index-parts/theme.js +1 -0
  39. package/dist/index-parts/translations.js +1 -0
  40. package/dist/index.js +1 -1
  41. package/dist/kernel-selector.js +1 -1
  42. package/dist/module-hooks/iframe-blocker.js +1 -1
  43. package/dist/provider.js +1 -1
  44. package/dist/styles.tw3.css +27 -3
  45. package/dist/version.js +1 -1
  46. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +4 -4
  47. package/dist-types/components/iab-panel/index.d.ts +0 -1
  48. package/dist-types/components/panel/index.d.ts +0 -1
  49. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +5 -0
  50. package/dist-types/components/panel-trigger/panel-trigger.d.ts +0 -8
  51. package/dist-types/components/preferences/index.d.ts +0 -1
  52. package/dist-types/components/shared/ui/accordion/accordion.d.ts +0 -1
  53. package/dist-types/components/shared/ui/collapsible/collapsible.d.ts +0 -1
  54. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +0 -1
  55. package/dist-types/components/shared/ui/switch/switch.d.ts +0 -1
  56. package/dist-types/components/shared/ui/tabs/tabs.d.ts +0 -1
  57. package/dist-types/devtools-launcher.d.ts +29 -0
  58. package/dist-types/draft-context.d.ts +31 -0
  59. package/dist-types/draft.d.ts +13 -36
  60. package/dist-types/gpp.d.ts +27 -0
  61. package/dist-types/hooks/use-iab-manager.d.ts +4 -4
  62. package/dist-types/hooks.d.ts +30 -5
  63. package/dist-types/index-parts/consent-banner.d.ts +7 -0
  64. package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
  65. package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
  66. package/dist-types/index-parts/consent-dialog.d.ts +7 -0
  67. package/dist-types/index-parts/consent-gate.d.ts +7 -0
  68. package/dist-types/index-parts/core.d.ts +8 -0
  69. package/dist-types/index-parts/draft.d.ts +7 -0
  70. package/dist-types/index-parts/hooks.d.ts +7 -0
  71. package/dist-types/index-parts/module-hooks.d.ts +7 -0
  72. package/dist-types/index-parts/provider.d.ts +7 -0
  73. package/dist-types/index-parts/theme.d.ts +8 -0
  74. package/dist-types/index-parts/translations.d.ts +7 -0
  75. package/dist-types/index.d.ts +15 -16
  76. package/dist-types/kernel-selector.d.ts +0 -8
  77. package/dist-types/module-hooks/iframe-blocker.d.ts +12 -7
  78. package/dist-types/provider.d.ts +47 -13
  79. package/dist-types/version.d.ts +1 -1
  80. package/docs/README.md +8 -5
  81. package/docs/concepts/choose-your-setup.md +22 -5
  82. package/docs/concepts/consent-state.md +60 -5
  83. package/docs/concepts/data-fetching.md +34 -11
  84. package/docs/customization/recipes.md +4 -5
  85. package/docs/customization/slots.md +3 -2
  86. package/docs/customization/stylesheets.md +29 -39
  87. package/docs/customization/tailwind.md +5 -5
  88. package/docs/customization/tokens.md +1 -1
  89. package/docs/customization/translations.md +33 -7
  90. package/docs/frameworks/react/components/consent-dialog-trigger.md +5 -0
  91. package/docs/frameworks/react/components/consent-dialog.md +2 -1
  92. package/docs/frameworks/react/components/consent-provider.md +6 -2
  93. package/docs/frameworks/react/components/dev-tools.md +24 -1
  94. package/docs/frameworks/react/gpp.md +163 -0
  95. package/docs/frameworks/react/headless.md +4 -4
  96. package/docs/frameworks/react/hooks.md +10 -5
  97. package/docs/frameworks/react/iab.md +24 -18
  98. package/docs/frameworks/react/quickstart.md +77 -16
  99. package/docs/frameworks/react/rendering.md +82 -11
  100. package/docs/frameworks/react/scripts.md +8 -3
  101. package/docs/frameworks/react/upgrade-v3.md +596 -0
  102. package/docs/frameworks/react/vendor-consent.md +6 -0
  103. package/docs/guides/banner-experiments.md +26 -5
  104. package/docs/guides/troubleshooting.md +3 -2
  105. package/docs/integrations/overview.md +28 -27
  106. package/docs/integrations/posthog.md +99 -11
  107. package/docs/integrations/sentry.md +416 -0
  108. package/docs/upgrade-v3.md +222 -314
  109. package/package.json +13 -8
  110. package/dist/ui-save.js +0 -1
  111. package/dist-types/ui-save.d.ts +0 -25
@@ -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. The Next.js example does this in the root
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
- In the example, `lib/flags.ts` stands in for the flag above and reads the arm
272
- from the URL. `ExperimentConsent` is the `Consent` wrapper from the
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 `examples/` runs this experiment outside the pages
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'` from
15
- `@c15t/nextjs`. `undefined` means no c15t provider has mounted on this
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 | 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
- | [Rybbit Analytics](./rybbit-analytics.md) | `rybbitAnalytics` | `measurement` | Waits for effective permission |
75
- | [Umami Analytics](./umami-analytics.md) | `umamiAnalytics` | `measurement` | Waits for effective permission |
76
- | [Vercel Analytics](./vercel-analytics.md) | `vercelAnalytics` | `measurement` | Waits for effective permission |
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
 
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  title: PostHog
3
3
  description: Load PostHog before or after measurement consent with the c15t
4
- posthog helper, choose its cookieless behavior, and sync consent with an SDK
5
- you already initialize.
4
+ posthog helper, choose its cookieless behavior, turn off PostHog modules you
5
+ do not use, and sync consent with an SDK you already initialize.
6
6
  icon: posthog
7
7
  group: integrations
8
8
  ---
@@ -257,15 +257,20 @@ A kernel you create yourself needs a loader from
257
257
 
258
258
  ## Options
259
259
 
260
- | Option | Default | Behavior |
261
- | ------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
262
- | `id` | Required | Project token, starting with `phc_`. The helper trims it. Empty or whitespace-only values throw. |
263
- | `region` | `'eu'` | `'eu'` or `'us'`. Picks the API, UI and loader hosts when you do not set them. |
264
- | `apiHost` | `https://eu.i.posthog.com` | API host for a proxy or self-hosted PostHog. Without `scriptUrl`, the loader URL becomes `<apiHost>/static/array.js`. |
265
- | `uiHost` | The region's UI host | UI host, for example `https://eu.posthog.com`. A custom `apiHost` with no `region` uses the API host. |
266
- | `scriptUrl` | `https://eu-assets.i.posthog.com/static/array.js` | Loader URL override. A blank value falls back to the default. |
267
- | `loadMode` | `'always'` | When the SDK loads. See the table below. |
268
- | `initOptions` | `{ cookieless_mode: 'on_reject', defaults: '2026-01-30' }` | Options passed to `posthog.init`, merged over the defaults. The helper sets `api_host` and `ui_host` after your values, so change hosts with the options above. |
260
+ | Option | Default | Behavior |
261
+ | ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
262
+ | `id` | Required | Project token, starting with `phc_`. The helper trims it. Empty or whitespace-only values throw. |
263
+ | `region` | `'eu'` | `'eu'` or `'us'`. Picks the API, UI and loader hosts when you do not set them. |
264
+ | `apiHost` | `https://eu.i.posthog.com` | API host for a proxy or self-hosted PostHog. Without `scriptUrl`, the loader URL becomes `<apiHost>/static/array.js`. |
265
+ | `uiHost` | The region's UI host | UI host, for example `https://eu.posthog.com`. A custom `apiHost` with no `region` uses the API host. |
266
+ | `scriptUrl` | `https://eu-assets.i.posthog.com/static/array.js` | Loader URL override. A blank value falls back to the default. |
267
+ | `loadMode` | `'always'` | When the SDK loads. See the table below. |
268
+ | `features.surveys` | Unset | `false` sets `disable_surveys: true` and skips `surveys.js`. See [turn off features you do not use](#turn-off-features-you-do-not-use). |
269
+ | `features.heatmaps` | Unset, follows the project | Sets `capture_heatmaps`. |
270
+ | `features.deadClicks` | Unset, follows the project | Sets `capture_dead_clicks`. |
271
+ | `features.webVitals` | Unset, follows the project | Sets `capture_performance: { web_vitals }`. Replay network timing keeps following the project. |
272
+ | `features.featureFlags` | Unset, follows the project | `false` sets `advanced_disable_feature_flags: true` and stops `/flags` requests. |
273
+ | `initOptions` | `{ cookieless_mode: 'on_reject', defaults: '2026-01-30' }` | Options passed to `posthog.init`, merged over the defaults and `features`. The helper sets `api_host` and `ui_host` after your values, so change hosts with the options above. |
269
274
 
270
275
  ## Loading and revocation
271
276
 
@@ -283,6 +288,78 @@ project; see
283
288
  Set `cookieless_mode: 'never'`, as in the example, if a refusal must stop
284
289
  capture.
285
290
 
291
+ ## Turn off features you do not use
292
+
293
+ After `array.js` loads, PostHog reads your `posthog.init` options and the
294
+ project's remote config, then decides which extra modules to download and
295
+ whether to request `/flags`. Set a `features` switch to `false` for each
296
+ feature your site does not use:
297
+
298
+ ```ts
299
+ posthog({
300
+ id: 'phc_YOUR_PROJECT_TOKEN',
301
+ region: 'eu',
302
+ loadMode: 'after-consent',
303
+ features: {
304
+ surveys: false,
305
+ heatmaps: false,
306
+ deadClicks: false,
307
+ webVitals: false,
308
+ featureFlags: false,
309
+ },
310
+ });
311
+ ```
312
+
313
+ | Switch | `false` sets | What PostHog skips |
314
+ | -------------- | -------------------------------------------- | --------------------------------------------------------------------------- |
315
+ | `surveys` | `disable_surveys: true` | `surveys.js`, about 29 KB |
316
+ | `heatmaps` | `capture_heatmaps: false` | Heatmap capture. With `deadClicks: false` too, `dead-clicks-autocapture.js` |
317
+ | `deadClicks` | `capture_dead_clicks: false` | `dead-clicks-autocapture.js`, about 8 KB, once heatmaps are off too |
318
+ | `webVitals` | `capture_performance: { web_vitals: false }` | `web-vitals-with-attribution.js`, about 6 KB |
319
+ | `featureFlags` | `advanced_disable_feature_flags: true` | `/flags` requests. The remote config still loads |
320
+
321
+ Sizes are brotli-compressed, measured from posthog-js 1.436.1.
322
+
323
+ An unset switch adds nothing to `posthog.init`. For heatmaps, dead clicks, web
324
+ vitals and feature flags, PostHog then follows the setting in your PostHog
325
+ project. `true` sets the opposite value and overrides the project. For
326
+ `surveys` and `featureFlags`, `true` is PostHog's own default, so it behaves
327
+ the same as unset.
328
+
329
+ Surveys work differently. PostHog downloads `surveys.js` whenever the remote
330
+ config includes a `surveys` value, even when surveys are off in the project.
331
+ `surveys: false` is the only way to skip it.
332
+
333
+ `initOptions` wins over a switch that sets the same key. A
334
+ `capture_performance` value in `initOptions` replaces the whole object that
335
+ `webVitals` builds.
336
+
337
+ ### Avoid PostHog's option traps
338
+
339
+ These rules also apply when you pass PostHog options yourself, in
340
+ `initOptions` or in your own `posthog.init`:
341
+
342
+ * Use `advanced_disable_feature_flags: true` to stop `/flags`, not
343
+ `advanced_disable_flags: true`. The second also stops PostHog loading the
344
+ remote config, so session replay never starts and other features fall back to
345
+ local config.
346
+ * With feature flags off, surveys that target a feature flag never show.
347
+ PostHog logs a warning about it. If you need those surveys, leave
348
+ `featureFlags` unset and pass
349
+ `advanced_only_evaluate_survey_feature_flags: true` instead. PostHog still
350
+ requests `/flags`, but evaluates only survey flags.
351
+ * While heatmaps are on, in `posthog.init` or in the project, PostHog loads
352
+ `dead-clicks-autocapture.js` whatever `capture_dead_clicks` says. Turn off
353
+ both to skip it.
354
+ * `capture_performance: false` turns off web vitals and session replay network
355
+ timing. `{ web_vitals: false }`, which `webVitals: false` sets, leaves network
356
+ timing to the project. `capture_performance: true` forces both on.
357
+
358
+ Product tours (`product-tours.js`, about 36 KB) and exception autocapture
359
+ (`exception-autocapture.js`, about 6 KB) load only when the project turns them
360
+ on. To keep them off whatever the project says, pass
361
+ `disable_product_tours: true` or `capture_exceptions: false` in `initOptions`.
362
+
286
363
  ## Guard your own capture calls
287
364
 
288
365
  Events your code sends need their own permission check. The `posthog` global
@@ -338,6 +415,11 @@ measurement is denied, so the SDK is told to opt out. `onConsentChange` runs on
338
415
  every later change. This does not delay the SDK import or its first request.
339
416
  `loadMode: 'disabled'` is not a substitute, because it syncs nothing.
340
417
 
418
+ `features` belongs to the helper, so it does nothing here. To skip modules you
419
+ do not use, pass the PostHog options from
420
+ [turn off features you do not use](#turn-off-features-you-do-not-use) to your
421
+ own `posthog.init`, for example `disable_surveys: true`.
422
+
341
423
  ## Measure opt-in rate
342
424
 
343
425
  If you run a banner experiment, the backend already counts visitors and
@@ -355,6 +437,12 @@ With `loadMode: 'always'`, `array.js` loads before a choice instead. Check that
355
437
  no capture request is sent while measurement is denied, unless you chose
356
438
  cookieless capture.
357
439
 
440
+ If you turned features off, allow measurement and filter the DevTools Network
441
+ panel by `posthog`. Reload the page. You should see `array.js`, the remote
442
+ config and capture requests, but no `/flags` request with `featureFlags: false`
443
+ and none of the modules you turned off, such as `surveys.js` or
444
+ `dead-clicks-autocapture.js`.
445
+
358
446
  Test in a private window with an opt-in policy. Open DevTools Network, disable
359
447
  the cache and filter by the vendor's domain:
360
448