@c15t/react 3.0.0-alpha.1 → 3.0.0-alpha.10
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 +96 -70
- package/README.md +21 -17
- package/SKILL.md +34 -0
- package/dist/aggregate-components.js +1 -1
- package/dist/chunk-warming.js +1 -1
- package/dist/component-hooks/use-headless-consent-ui.js +1 -1
- package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
- package/dist/component-hooks/use-panel-trigger.js +1 -1
- package/dist/components/consent-gate/atoms.js +1 -0
- package/dist/components/consent-gate/consent-gate.js +2 -0
- package/dist/components/consent-gate/frame.js +2 -0
- package/dist/components/consent-gate/index.js +2 -0
- package/dist/components/consent-preferences-link/consent-preferences-link.js +1 -1
- package/dist/components/iab-panel/atoms/card.js +1 -1
- package/dist/components/iab-panel/atoms/feature-item.js +2 -0
- package/dist/components/iab-panel/atoms/header.js +1 -1
- package/dist/components/iab-panel/atoms/purpose-item.js +1 -1
- package/dist/components/iab-panel/atoms/root.js +1 -1
- package/dist/components/iab-panel/atoms/stack-item.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/iab-panel.js +1 -1
- package/dist/components/iab-panel/index.js +1 -1
- package/dist/components/iab-prompt/atoms/overlay.js +1 -1
- package/dist/components/iab-prompt/atoms/root.js +1 -1
- package/dist/components/panel/atoms/card.js +1 -1
- package/dist/components/panel/atoms/overlay.js +1 -1
- package/dist/components/panel/atoms/root.js +1 -1
- package/dist/components/panel/panel.js +1 -1
- package/dist/components/panel-trigger/atoms/button.js +1 -1
- package/dist/components/panel-trigger/atoms/icon.js +1 -1
- package/dist/components/panel-trigger/atoms/root.js +1 -1
- package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
- package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -0
- package/dist/components/panel-trigger/panel-trigger-toolbar.js +1 -1
- package/dist/components/panel-trigger/panel-trigger.js +1 -1
- package/dist/components/preferences/atoms/accordion.js +1 -1
- package/dist/components/preferences/atoms/root.js +1 -1
- package/dist/components/preferences/atoms/vendor-list.js +1 -0
- package/dist/components/preferences/index.js +1 -1
- package/dist/components/prompt/atoms/early-tap-script.js +2 -0
- package/dist/components/prompt/atoms/overlay.js +1 -1
- package/dist/components/prompt/atoms/root.js +1 -1
- package/dist/components/prompt/components.js +1 -1
- package/dist/components/prompt/policy-actions.js +1 -1
- package/dist/components/prompt/prompt.js +1 -1
- package/dist/components/shared/dialog-sheets.js +1 -0
- package/dist/components/shared/first-paint-sheets.js +1 -0
- package/dist/components/shared/iab-dialog-sheets.js +1 -0
- package/dist/components/shared/iab-first-paint-sheets.js +1 -0
- package/dist/components/shared/libs/compose-refs.js +1 -0
- package/dist/components/shared/libs/slot.js +1 -1
- package/dist/components/shared/primitives/button.js +1 -1
- package/dist/components/shared/primitives/legal-links/index.js +1 -1
- package/dist/components/shared/streamed-surface.js +2 -0
- package/dist/components/shared/surface-styles.js +2 -0
- package/dist/components/shared/ui/dialog/dialog.js +1 -1
- package/dist/components/shared/ui/icon.js +1 -1
- package/dist/components/shared/ui/logo.js +1 -1
- package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
- package/dist/consent-gate.js +2 -0
- package/dist/consent-theme.js +1 -0
- package/dist/context/consent-tracking-context.js +1 -1
- package/dist/context/index.js +1 -1
- package/dist/context.js +1 -1
- package/dist/devtools-launcher.js +2 -0
- package/dist/devtools.js +1 -1
- package/dist/draft-context.js +2 -0
- package/dist/draft.js +1 -1
- package/dist/external-iab-context.js +1 -1
- package/dist/gpp.js +2 -0
- package/dist/headless.js +1 -1
- package/dist/hooks/use-color-scheme.js +1 -1
- package/dist/hooks/use-committed-ref.js +2 -0
- package/dist/hooks/use-focus-trap.js +1 -1
- package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
- package/dist/hooks/use-iab-policy-guard.js +1 -0
- package/dist/hooks/use-late-entry.js +2 -0
- package/dist/hooks.js +1 -1
- package/dist/iab/styles.tw3.css +5 -45
- package/dist/iab-context.js +1 -1
- package/dist/index-parts/consent-banner.js +1 -0
- package/dist/index-parts/consent-dialog-link.js +1 -0
- package/dist/index-parts/consent-dialog-trigger.js +1 -0
- package/dist/index-parts/consent-dialog.js +1 -0
- package/dist/index-parts/consent-gate.js +1 -0
- package/dist/index-parts/core.js +1 -0
- package/dist/index-parts/draft.js +1 -0
- package/dist/index-parts/frame.js +1 -0
- package/dist/index-parts/hooks.js +1 -0
- package/dist/index-parts/hosted.js +1 -0
- package/dist/index-parts/module-hooks.js +1 -0
- package/dist/index-parts/offline.js +1 -0
- package/dist/index-parts/presets.js +1 -0
- package/dist/index-parts/provider.js +1 -0
- package/dist/index-parts/theme.js +1 -0
- package/dist/index-parts/translations.js +1 -0
- package/dist/index.js +1 -1
- package/dist/kernel-selector.js +1 -0
- package/dist/modes.js +1 -0
- package/dist/module-hooks/iframe-blocker.js +1 -1
- package/dist/module-hooks/network-blocker.js +1 -1
- package/dist/module-hooks/network-hold.js +2 -0
- package/dist/module-hooks/persistence.js +1 -1
- package/dist/panel-link.js +1 -1
- package/dist/panel-trigger.js +1 -1
- package/dist/panel.js +1 -1
- package/dist/postcss-tailwind3.js +1 -0
- package/dist/preferences.js +1 -2
- package/dist/provider.js +1 -1
- package/dist/server/fetch-ssr-data.js +1 -1
- package/dist/server/headers.js +1 -1
- package/dist/server/normalize-url.js +1 -1
- package/dist/styles.tw3.css +33 -51
- package/dist/transports/hosted.js +1 -0
- package/dist/transports/manifest.js +1 -0
- package/dist/transports/offline.js +1 -1
- package/dist/utils/deferred-module.js +2 -0
- package/dist/utils/merge-slot-props.js +1 -1
- package/dist/utils/theme-utils.js +1 -1
- package/dist/version.js +1 -1
- package/dist-types/aggregate-components.d.ts +2 -2
- package/dist-types/chunk-warming.d.ts +45 -8
- package/dist-types/component-hooks/use-headless-consent-ui.d.ts +5 -5
- package/dist-types/component-hooks/use-headless-iab-consent-ui.d.ts +2 -2
- package/dist-types/components/consent-gate/atoms.d.ts +11 -0
- package/dist-types/components/consent-gate/consent-gate.d.ts +2 -0
- package/dist-types/components/consent-gate/frame.d.ts +19 -0
- package/dist-types/components/consent-gate/index.d.ts +25 -0
- package/dist-types/components/{frame → consent-gate}/types.d.ts +7 -7
- package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
- package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
- package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
- package/dist-types/components/iab-panel/atoms/purpose-item.d.ts +5 -2
- package/dist-types/components/iab-panel/atoms/stack-item.d.ts +7 -2
- package/dist-types/components/iab-panel/atoms/vendor-list.d.ts +1 -1
- package/dist-types/components/iab-panel/iab-panel.d.ts +3 -3
- package/dist-types/components/iab-panel/index.d.ts +29 -26
- package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
- package/dist-types/components/iab-prompt/iab-prompt.d.ts +3 -3
- package/dist-types/components/iab-prompt/index.d.ts +19 -19
- package/dist-types/components/panel/atoms/card.d.ts +5 -4
- package/dist-types/components/panel/atoms/overlay.d.ts +1 -1
- package/dist-types/components/panel/index.d.ts +8 -8
- package/dist-types/components/panel/panel.d.ts +3 -3
- package/dist-types/components/panel-link/index.d.ts +1 -1
- package/dist-types/components/panel-trigger/atoms/button.d.ts +1 -1
- package/dist-types/components/panel-trigger/atoms/icon.d.ts +1 -1
- package/dist-types/components/panel-trigger/atoms/root.d.ts +2 -2
- package/dist-types/components/panel-trigger/atoms/toolbar.d.ts +2 -2
- package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +25 -0
- package/dist-types/components/panel-trigger/index.d.ts +4 -4
- package/dist-types/components/panel-trigger/panel-trigger-toolbar.d.ts +1 -1
- package/dist-types/components/panel-trigger/panel-trigger.d.ts +9 -17
- package/dist-types/components/panel-trigger/types.d.ts +1 -1
- package/dist-types/components/preferences/atoms/accordion.d.ts +6 -4
- package/dist-types/components/preferences/atoms/button.d.ts +1 -1
- package/dist-types/components/preferences/atoms/footer.d.ts +1 -1
- package/dist-types/components/preferences/atoms/root.d.ts +1 -1
- package/dist-types/components/preferences/atoms/vendor-list.d.ts +23 -0
- package/dist-types/components/preferences/index.d.ts +14 -12
- package/dist-types/components/preferences/policy-actions.d.ts +2 -2
- package/dist-types/components/preferences/preferences.d.ts +1 -1
- package/dist-types/components/preferences/types.d.ts +1 -1
- package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
- package/dist-types/components/prompt/atoms/root.d.ts +4 -0
- package/dist-types/components/prompt/components.d.ts +9 -3
- package/dist-types/components/prompt/index.d.ts +12 -12
- package/dist-types/components/prompt/policy-actions.d.ts +2 -2
- package/dist-types/components/prompt/prompt.d.ts +5 -1
- package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
- package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
- package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
- package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
- package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
- package/dist-types/components/shared/policy-actions.d.ts +2 -2
- package/dist-types/components/shared/primitives/box.d.ts +1 -1
- package/dist-types/components/shared/primitives/button.d.ts +2 -2
- package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
- package/dist-types/components/shared/streamed-surface.d.ts +23 -0
- package/dist-types/components/shared/surface-styles.d.ts +36 -0
- package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
- package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
- package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
- package/dist-types/components/shared/ui/branding.d.ts +1 -1
- package/dist-types/components/shared/ui/button/button.d.ts +1 -1
- package/dist-types/components/shared/ui/button/index.d.ts +2 -2
- package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
- package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
- package/dist-types/components/shared/ui/icon.d.ts +2 -1
- package/dist-types/components/shared/ui/logo.d.ts +0 -10
- package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
- package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +13 -0
- package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
- package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
- package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
- package/dist-types/consent-gate.d.ts +1 -0
- package/dist-types/consent-theme.d.ts +38 -0
- package/dist-types/context/consent-tracking-context.d.ts +10 -0
- package/dist-types/context/index.d.ts +3 -3
- package/dist-types/context/theme-context.d.ts +3 -3
- package/dist-types/context.d.ts +25 -1
- package/dist-types/devtools-launcher.d.ts +29 -0
- package/dist-types/devtools.d.ts +13 -2
- package/dist-types/draft-context.d.ts +31 -0
- package/dist-types/draft.d.ts +81 -9
- package/dist-types/gpp.d.ts +27 -0
- package/dist-types/headless.d.ts +6 -7
- package/dist-types/hooks/use-committed-ref.d.ts +24 -0
- package/dist-types/hooks/use-focus-trap.d.ts +3 -1
- package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
- package/dist-types/hooks/use-iab-manager.d.ts +5 -5
- package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
- package/dist-types/hooks/use-late-entry.d.ts +14 -0
- package/dist-types/hooks/use-theme.d.ts +1 -1
- package/dist-types/hooks.d.ts +106 -11
- package/dist-types/iab.d.ts +7 -7
- package/dist-types/index-parts/consent-banner.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog.d.ts +7 -0
- package/dist-types/index-parts/consent-gate.d.ts +7 -0
- package/dist-types/index-parts/core.d.ts +7 -0
- package/dist-types/index-parts/draft.d.ts +7 -0
- package/dist-types/index-parts/frame.d.ts +7 -0
- package/dist-types/index-parts/hooks.d.ts +7 -0
- package/dist-types/index-parts/hosted.d.ts +7 -0
- package/dist-types/index-parts/module-hooks.d.ts +7 -0
- package/dist-types/index-parts/offline.d.ts +7 -0
- package/dist-types/index-parts/presets.d.ts +7 -0
- package/dist-types/index-parts/provider.d.ts +7 -0
- package/dist-types/index-parts/theme.d.ts +8 -0
- package/dist-types/index-parts/translations.d.ts +7 -0
- package/dist-types/index.d.ts +32 -28
- package/dist-types/kernel-selector.d.ts +68 -0
- package/dist-types/modes.d.ts +26 -0
- package/dist-types/module-hooks/iframe-blocker.d.ts +33 -1
- package/dist-types/module-hooks/network-blocker.d.ts +23 -0
- package/dist-types/module-hooks/network-hold.d.ts +47 -0
- package/dist-types/module-hooks.d.ts +4 -4
- package/dist-types/panel-link.d.ts +1 -1
- package/dist-types/panel-trigger.d.ts +1 -1
- package/dist-types/panel.d.ts +13 -1
- package/dist-types/postcss-tailwind3.d.ts +21 -0
- package/dist-types/preferences.d.ts +12 -1
- package/dist-types/primitives/accordion.d.ts +1 -1
- package/dist-types/primitives/button.d.ts +1 -1
- package/dist-types/primitives/collapsible.d.ts +1 -1
- package/dist-types/primitives/dialog.d.ts +1 -1
- package/dist-types/primitives/preference-item.d.ts +1 -1
- package/dist-types/primitives/switch.d.ts +1 -1
- package/dist-types/primitives/tabs.d.ts +1 -1
- package/dist-types/primitives.d.ts +7 -7
- package/dist-types/prompt.d.ts +1 -1
- package/dist-types/provider.d.ts +169 -28
- package/dist-types/server/fetch-ssr-data.d.ts +1 -1
- package/dist-types/server/headers.d.ts +14 -4
- package/dist-types/server/index.d.ts +5 -5
- package/dist-types/server/normalize-url.d.ts +14 -7
- package/dist-types/server/types.d.ts +12 -1
- package/dist-types/theme-provider.d.ts +2 -2
- package/dist-types/transports/hosted.d.ts +36 -0
- package/dist-types/transports/manifest.d.ts +48 -0
- package/dist-types/transports/offline.d.ts +7 -16
- package/dist-types/types/index.d.ts +3 -3
- package/dist-types/types/manager.d.ts +1 -1
- package/dist-types/types/theme/index.d.ts +1 -1
- package/dist-types/ui-config-context.d.ts +14 -2
- package/dist-types/utils/deferred-module.d.ts +13 -0
- package/dist-types/utils/index.d.ts +4 -4
- package/dist-types/utils/merge-slot-props.d.ts +17 -3
- package/dist-types/utils/merge-styles.d.ts +1 -1
- package/dist-types/utils/theme-utils.d.ts +18 -3
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +96 -70
- package/docs/assets/v3/bottom-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/brand-preferences.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/headless-bar-html.png +0 -0
- package/docs/assets/v3/headless-bar-mobile.png +0 -0
- package/docs/assets/v3/headless-bar.png +0 -0
- package/docs/assets/v3/slim-bar.png +0 -0
- package/docs/concepts/choose-your-setup.md +138 -0
- package/docs/concepts/consent-categories.md +84 -0
- package/docs/concepts/consent-state.md +421 -0
- package/docs/concepts/data-fetching.md +270 -0
- package/docs/concepts/how-consent-works.md +123 -0
- package/docs/concepts/modes.md +278 -0
- package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +8 -60
- package/docs/customization/class-names.md +202 -0
- package/docs/customization/dark-mode.md +157 -0
- package/docs/customization/motion.md +128 -0
- package/docs/customization/overview.md +68 -33
- package/docs/customization/recipes.md +838 -47
- package/docs/customization/slots.md +217 -35
- package/docs/customization/stylesheets.md +248 -0
- package/docs/customization/tailwind.md +863 -0
- package/docs/customization/tokens.md +167 -36
- package/docs/customization/translations.md +87 -3
- package/docs/frameworks/react/callbacks.md +118 -0
- package/docs/frameworks/react/clear-on-revocation.md +106 -0
- package/docs/frameworks/react/components/consent-banner.md +48 -27
- package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
- package/docs/frameworks/react/components/consent-dialog-trigger.md +86 -76
- package/docs/frameworks/react/components/consent-dialog.md +111 -58
- package/docs/frameworks/react/components/{frame.md → consent-gate.md} +70 -49
- package/docs/frameworks/react/components/consent-provider.md +140 -0
- package/docs/frameworks/react/components/consent-widget.md +69 -24
- package/docs/frameworks/react/components/dev-tools.md +76 -85
- package/docs/frameworks/react/components.md +113 -0
- package/docs/frameworks/react/compose.md +354 -0
- package/docs/frameworks/react/content-security-policy.md +143 -0
- package/docs/frameworks/react/customize.md +258 -0
- package/docs/frameworks/react/embeds.md +89 -0
- package/docs/frameworks/react/gpp.md +163 -0
- package/docs/frameworks/react/headless.md +4 -4
- package/docs/frameworks/react/hooks.md +70 -0
- package/docs/frameworks/react/iab.md +259 -0
- package/docs/frameworks/react/network-blocker.md +140 -0
- package/docs/frameworks/react/quickstart.md +220 -102
- package/docs/frameworks/react/rendering.md +263 -0
- package/docs/frameworks/react/scripts.md +114 -0
- package/docs/frameworks/react/translations.md +155 -0
- package/docs/frameworks/react/troubleshooting.md +188 -61
- package/docs/frameworks/react/upgrade-v3.md +651 -0
- package/docs/frameworks/react/vendor-consent.md +188 -0
- package/docs/guides/banner-experiments.md +672 -0
- package/docs/guides/troubleshooting.md +192 -54
- package/docs/guides/verify-consent.md +96 -49
- package/docs/integrations/adobe-analytics.md +177 -183
- package/docs/integrations/ahrefs-analytics.md +162 -178
- package/docs/integrations/amplitude.md +172 -180
- package/docs/integrations/building-integrations.md +137 -33
- package/docs/integrations/clearbit.md +164 -178
- package/docs/integrations/cloudflare-web-analytics.md +166 -180
- package/docs/integrations/cloudflare-zaraz.md +333 -0
- package/docs/integrations/crisp.md +174 -182
- package/docs/integrations/databuddy.md +167 -197
- package/docs/integrations/fathom-analytics.md +168 -180
- package/docs/integrations/front-chat.md +308 -0
- package/docs/integrations/google-maps.md +142 -108
- package/docs/integrations/google-tag-manager.md +258 -185
- package/docs/integrations/google-tag.md +226 -182
- package/docs/integrations/heap.md +173 -179
- package/docs/integrations/hightouch.md +171 -181
- package/docs/integrations/hotjar.md +169 -179
- package/docs/integrations/intercom.md +193 -177
- package/docs/integrations/klaviyo.md +473 -0
- package/docs/integrations/linkedin-insights.md +184 -174
- package/docs/integrations/logrocket.md +170 -180
- package/docs/integrations/matomo-analytics.md +198 -202
- package/docs/integrations/meta-pixel.md +198 -174
- package/docs/integrations/microsoft-clarity.md +173 -179
- package/docs/integrations/microsoft-uet.md +158 -178
- package/docs/integrations/mixpanel-analytics.md +165 -184
- package/docs/integrations/one-dollar-stats.md +292 -0
- package/docs/integrations/openai-pixel.md +214 -325
- package/docs/integrations/overview.md +152 -83
- package/docs/integrations/pinterest-tag.md +315 -0
- package/docs/integrations/pirsch.md +179 -183
- package/docs/integrations/plausible-analytics.md +182 -182
- package/docs/integrations/posthog.md +323 -264
- package/docs/integrations/promptwatch.md +164 -178
- package/docs/integrations/reddit-pixel.md +195 -181
- package/docs/integrations/rudderstack.md +211 -210
- package/docs/integrations/rybbit-analytics.md +181 -184
- package/docs/integrations/segment.md +192 -178
- package/docs/integrations/sentry.md +402 -0
- package/docs/integrations/snapchat-pixel.md +196 -180
- package/docs/integrations/tiktok-pixel.md +181 -174
- package/docs/integrations/umami-analytics.md +173 -182
- package/docs/integrations/vercel-analytics.md +176 -181
- package/docs/integrations/x-pixel.md +186 -174
- package/docs/integrations/youtube.md +150 -111
- package/package.json +48 -17
- package/dist/component-hooks/use-manager.js +0 -2
- package/dist/components/frame/atoms.js +0 -1
- package/dist/components/frame/frame.js +0 -2
- package/dist/components/frame/index.js +0 -2
- package/dist/frame.js +0 -2
- package/dist/ui-save.js +0 -1
- package/dist-types/component-hooks/use-manager.d.ts +0 -40
- package/dist-types/components/frame/atoms.d.ts +0 -11
- package/dist-types/components/frame/frame.d.ts +0 -2
- package/dist-types/components/frame/index.d.ts +0 -24
- package/dist-types/frame.d.ts +0 -1
- package/dist-types/ui-save.d.ts +0 -5
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/frameworks/react/components/consent-manager-provider.md +0 -71
- package/docs/frameworks/react/concepts/consent-categories.md +0 -33
- package/docs/frameworks/react/hooks/use-consent-manager/overview.md +0 -58
- package/docs/frameworks/react/iab/overview.md +0 -53
- package/docs/frameworks/react/script-loader.md +0 -63
- package/docs/frameworks/react/styling/overview.md +0 -217
- package/docs/guides/consent-state.md +0 -60
- package/docs/guides/data-fetching.md +0 -163
- package/docs/guides/deployment-modes.md +0 -63
- package/docs/integrations/clear-on-revocation.md +0 -167
- package/docs/upgrade-v3.md +0 -334
- package/readme.json +0 -48
- package/src/iab/styles.css +0 -11
- package/src/styles.css +0 -10
- /package/dist/components/{frame → consent-gate}/types.js +0 -0
|
@@ -1,143 +1,261 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Quickstart
|
|
3
|
-
description:
|
|
4
|
-
|
|
3
|
+
description: Add a consent banner, preferences dialog and consent-gated scripts
|
|
4
|
+
to a React single-page app built with Vite, using Inth as the consent backend.
|
|
5
5
|
group: frameworks
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Before you start
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
This guide sets up a client-rendered React app built with Vite. The build
|
|
11
|
+
bundles your project's public policy. The browser loads the page, resolves the
|
|
12
|
+
visitor's policy from that bundle, then shows the banner or runs the scripts
|
|
13
|
+
the visitor allowed. Regional policies may still need a backend location
|
|
14
|
+
request.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
* **Next.js or TanStack Start?** Use that framework's guide from the
|
|
17
|
+
[framework list](https://v3.c15t.com/docs/frameworks). Their adapters resolve consent on the
|
|
18
|
+
server.
|
|
19
|
+
* **React Router framework mode, Remix or another server-rendered React app?**
|
|
20
|
+
This guide still works. The server renders the page without consent state and
|
|
21
|
+
the browser resolves it after hydration. Read
|
|
22
|
+
[rendering](./rendering.md#react-router-remix-and-other-server-rendered-apps)
|
|
23
|
+
for what that means for the first paint.
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
You need an [Inth](https://inth.com) project. In the project, set the policy
|
|
26
|
+
rules for your regions, add your site's origin to the trusted origins, and copy
|
|
27
|
+
the backend URL.
|
|
28
|
+
[Choose your setup](../../concepts/choose-your-setup.md#decide-who-runs-the-consent-backend)
|
|
29
|
+
covers self-hosting and offline mode.
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
Bundling the policy at build time is the recommended production setup.
|
|
32
|
+
Rebuild after changing policies, translations or vendors.
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
Remove any existing loaders for them so each script loads once.
|
|
34
|
+
## Install c15t
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
| Package manager | Command |
|
|
37
|
+
| :-------------- | :------------------------------------------------ |
|
|
38
|
+
| npm | `npm install c15t@alpha @c15t/integrations@alpha` |
|
|
39
|
+
| pnpm | `pnpm add c15t@alpha @c15t/integrations@alpha` |
|
|
40
|
+
| yarn | `yarn add c15t@alpha @c15t/integrations@alpha` |
|
|
41
|
+
| bun | `bun add c15t@alpha @c15t/integrations@alpha` |
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
`c15t` contains the React adapter at `c15t/react` and the Vite plugin at
|
|
44
|
+
`c15t/build`. `@c15t/integrations` holds the vendor loaders used below.
|
|
45
|
+
|
|
46
|
+
## Bundle your policy
|
|
47
|
+
|
|
48
|
+
Add `consentManifest` to `vite.config.ts`:
|
|
49
|
+
|
|
50
|
+
```ts title="vite.config.ts"
|
|
51
|
+
import react from '@vitejs/plugin-react';
|
|
52
|
+
import { consentManifest } from 'c15t/build';
|
|
53
|
+
import { defineConfig } from 'vite';
|
|
54
|
+
|
|
55
|
+
export default defineConfig({ plugins: [react(), consentManifest()] });
|
|
39
56
|
```
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`loadMode: 'after-consent'` and disables cookieless capture with
|
|
48
|
-
`cookieless_mode: 'never'`. X Pixel waits for marketing permission.
|
|
49
|
-
|
|
50
|
-
```ts title="src/scripts.ts"
|
|
51
|
-
import { posthog } from '@c15t/scripts/posthog';
|
|
52
|
-
import { xPixel } from '@c15t/scripts/x-pixel';
|
|
53
|
-
|
|
54
|
-
const posthogKey = import.meta.env.VITE_POSTHOG_KEY;
|
|
55
|
-
const xPixelId = import.meta.env.VITE_X_PIXEL_ID;
|
|
56
|
-
|
|
57
|
-
export const scripts = [
|
|
58
|
-
...(posthogKey
|
|
59
|
-
? [
|
|
60
|
-
posthog({
|
|
61
|
-
apiHost: import.meta.env.VITE_POSTHOG_HOST,
|
|
62
|
-
id: posthogKey,
|
|
63
|
-
initOptions: { cookieless_mode: 'never' },
|
|
64
|
-
loadMode: 'after-consent',
|
|
65
|
-
}),
|
|
66
|
-
]
|
|
67
|
-
: []),
|
|
68
|
-
...(xPixelId ? [xPixel({ pixelId: xPixelId })] : []),
|
|
69
|
-
];
|
|
58
|
+
The plugin reads `VITE_C15T_BACKEND_URL`. Set it to your project's backend URL,
|
|
59
|
+
exactly as Inth shows it, including any path prefix, in `.env` or wherever you
|
|
60
|
+
build:
|
|
61
|
+
|
|
62
|
+
```sh title=".env"
|
|
63
|
+
VITE_C15T_BACKEND_URL=https://your-project.inth.app
|
|
70
64
|
```
|
|
71
65
|
|
|
72
|
-
|
|
66
|
+
If your app already sets `VITE_INTH_PROJECT_URL` for other Inth SDKs, that works
|
|
67
|
+
too. When both are set, `VITE_C15T_BACKEND_URL` wins.
|
|
68
|
+
|
|
69
|
+
`consentManifest` downloads your project's public policy from `/manifest`
|
|
70
|
+
during `vite build`, or in `vite dev` when the app first loads it, and serves
|
|
71
|
+
it to your app as `snapshot`
|
|
72
|
+
from `c15t/generated`. No file is written into your project.
|
|
73
|
+
|
|
74
|
+
The snapshot is fixed at build time:
|
|
75
|
+
|
|
76
|
+
* Rebuild after changing policies, translations or vendors. If your CI caches
|
|
77
|
+
build output, force a fresh build.
|
|
78
|
+
* Consent choices still go to the backend.
|
|
79
|
+
|
|
80
|
+
The build reads the backend URL from your public backend URL variable when
|
|
81
|
+
the config doesn't pass one: `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js,
|
|
82
|
+
`NUXT_PUBLIC_C15T_BACKEND_URL` in Nuxt, `PUBLIC_C15T_BACKEND_URL` in Astro,
|
|
83
|
+
Svelte and SvelteKit, and `VITE_C15T_BACKEND_URL` in TanStack Start and other
|
|
84
|
+
Vite apps. Each also reads the matching Inth variable, such as
|
|
85
|
+
`NEXT_PUBLIC_INTH_PROJECT_URL`, when the c15t one is unset. See
|
|
86
|
+
[set the backend URL](../../concepts/modes.md#set-the-backend-url).
|
|
87
|
+
|
|
88
|
+
The fetch waits at most 10 seconds. When it fails, or no backend URL is set,
|
|
89
|
+
every framework does the same thing:
|
|
90
|
+
|
|
91
|
+
| Command | Default when the fetch fails |
|
|
92
|
+
| ------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
93
|
+
| Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
|
|
94
|
+
| Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning, and the server fetches the policy at runtime. |
|
|
95
|
+
|
|
96
|
+
Set `onBuildError` to use one behaviour for both. `'fail'` stops dev too.
|
|
97
|
+
`'runtime'` lets a production build finish, and the server fetches the policy
|
|
98
|
+
at runtime. The `C15T_ON_BUILD_ERROR` environment variable overrides the
|
|
99
|
+
option, so you can deploy during a backend outage without a code change:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
C15T_ON_BUILD_ERROR=runtime npm run build
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Turborepo's strict environment mode hides undeclared variables from tasks, so
|
|
106
|
+
list `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
|
|
107
|
+
|
|
108
|
+
The build skips the fetch, without an error, when it can't use a snapshot,
|
|
109
|
+
for example when the backend URL is relative. With `onBuildError: 'fail'`, a
|
|
110
|
+
relative URL stops the build. [Consent modes](../../concepts/modes.md#what-happens-when-the-download-fails)
|
|
111
|
+
lists every case.
|
|
112
|
+
|
|
113
|
+
`vite build` and `vite dev` fetch the manifest when they start. `vite preview`
|
|
114
|
+
serves the last build without fetching. The plugin writes no file into your
|
|
115
|
+
app, so there is nothing to keep out of Git. `c15t/generated` ships its own
|
|
116
|
+
types, so `tsc`, `vue-tsc` and `svelte-check` pass on a fresh checkout without
|
|
117
|
+
a build first.
|
|
118
|
+
|
|
119
|
+
The plugin can't see the options your app passes to `manifest()`. When the
|
|
120
|
+
app passes `source: 'runtime'` or `manifestURL`, set
|
|
121
|
+
`consentManifest({ source: 'runtime' })` too. The build then downloads no
|
|
122
|
+
manifest, so it doesn't fail when the backend's `/manifest` is down.
|
|
123
|
+
|
|
124
|
+
## Mount the provider
|
|
125
|
+
|
|
126
|
+
Create `src/consent.tsx`. `ConsentProvider` creates the consent runtime and
|
|
127
|
+
loads the scripts. `ConsentBanner` and `ConsentDialog` render when the policy
|
|
128
|
+
asks for them, and `ConsentDialogLink` keeps preferences reachable after the
|
|
129
|
+
banner closes.
|
|
73
130
|
|
|
74
131
|
```tsx title="src/consent.tsx"
|
|
75
|
-
import
|
|
132
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
76
133
|
import {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
134
|
+
ConsentBanner,
|
|
135
|
+
ConsentDialog,
|
|
136
|
+
ConsentDialogLink,
|
|
137
|
+
ConsentProvider,
|
|
138
|
+
manifest,
|
|
82
139
|
} from 'c15t/react';
|
|
83
|
-
import '
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
140
|
+
import type { ReactNode } from 'react';
|
|
141
|
+
|
|
142
|
+
const options = {
|
|
143
|
+
// The policy the build downloaded from VITE_C15T_BACKEND_URL.
|
|
144
|
+
mode: manifest(),
|
|
145
|
+
scripts: [
|
|
146
|
+
posthog({
|
|
147
|
+
id: 'phc_your_project_key',
|
|
148
|
+
initOptions: { cookieless_mode: 'never' },
|
|
149
|
+
loadMode: 'after-consent',
|
|
150
|
+
}),
|
|
151
|
+
],
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const Consent = ({ children }: { children: ReactNode }) => (
|
|
155
|
+
<ConsentProvider options={options}>
|
|
156
|
+
{children}
|
|
157
|
+
<ConsentBanner />
|
|
158
|
+
<ConsentDialog />
|
|
159
|
+
<footer>
|
|
160
|
+
<ConsentDialogLink>Privacy settings</ConsentDialogLink>
|
|
161
|
+
</footer>
|
|
162
|
+
</ConsentProvider>
|
|
163
|
+
);
|
|
100
164
|
```
|
|
101
165
|
|
|
166
|
+
`manifest()` resolves the policy from the bundled snapshot in the browser,
|
|
167
|
+
with no `/manifest` request. It reads the snapshot and the backend URL from
|
|
168
|
+
what `consentManifest` downloaded. Consent choices go to that backend, and so
|
|
169
|
+
does an `/init` request when the policy depends on the visitor's country or
|
|
170
|
+
region and the browser does not know it. That request returns the backend's
|
|
171
|
+
current policy, which can be newer than the snapshot. Vite reads
|
|
172
|
+
`VITE_C15T_BACKEND_URL` at build time, so set it before you build, not when
|
|
173
|
+
you serve the files.
|
|
174
|
+
|
|
175
|
+
To apply policy edits without rebuilding, use `hosted()` from `c15t/react` as
|
|
176
|
+
the mode. The browser then requests the policy from `/init` on every page load.
|
|
177
|
+
[Consent modes](../../concepts/modes.md) compares the three modes.
|
|
178
|
+
|
|
179
|
+
PostHog waits for measurement permission, so your policy must include that
|
|
180
|
+
category. Replace `phc_your_project_key` with your PostHog project key.
|
|
181
|
+
PostHog uses its EU host; add `region: 'us'` for a US project. Remove any
|
|
182
|
+
other PostHog loader from your app so PostHog loads once.
|
|
183
|
+
|
|
184
|
+
`loadMode: 'after-consent'` keeps the PostHog SDK off the page until the
|
|
185
|
+
visitor allows measurement. `cookieless_mode: 'never'` turns off PostHog's
|
|
186
|
+
cookieless capture. See [integrations](../../integrations/overview.md) for other
|
|
187
|
+
vendors.
|
|
188
|
+
|
|
189
|
+
Wrap your app with it in `src/main.tsx`:
|
|
190
|
+
|
|
102
191
|
```tsx title="src/main.tsx"
|
|
103
192
|
import { createRoot } from 'react-dom/client';
|
|
104
|
-
|
|
193
|
+
|
|
194
|
+
import { App } from './app';
|
|
105
195
|
import { Consent } from './consent';
|
|
106
196
|
|
|
107
197
|
const root = document.getElementById('root');
|
|
108
|
-
if (!root)
|
|
109
|
-
|
|
198
|
+
if (!root) {
|
|
199
|
+
throw new Error('Missing #root element');
|
|
200
|
+
}
|
|
201
|
+
createRoot(root).render(
|
|
202
|
+
<Consent>
|
|
203
|
+
<App />
|
|
204
|
+
</Consent>
|
|
205
|
+
);
|
|
110
206
|
```
|
|
111
207
|
|
|
112
|
-
Keep `Consent` mounted
|
|
113
|
-
|
|
114
|
-
|
|
208
|
+
Keep `Consent` mounted for the life of the app. If you use a client-side
|
|
209
|
+
router, render the router inside `Consent` so navigation does not remount the
|
|
210
|
+
provider. There is no stylesheet to import: the banner and dialog render their
|
|
211
|
+
own rules as `<style>` elements.
|
|
115
212
|
|
|
116
|
-
## Gate
|
|
213
|
+
## Gate your own features
|
|
117
214
|
|
|
118
|
-
|
|
215
|
+
Read a category's permission with `useConsent` inside the provider:
|
|
216
|
+
|
|
217
|
+
```tsx title="src/marketing-banner.tsx"
|
|
119
218
|
import { useConsent } from 'c15t/react';
|
|
120
219
|
|
|
121
|
-
export function
|
|
122
|
-
|
|
220
|
+
export function MarketingBanner() {
|
|
221
|
+
return useConsent('marketing') ? <aside>Spring sale</aside> : null;
|
|
123
222
|
}
|
|
124
223
|
```
|
|
125
224
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
225
|
+
`useConsent` returns the permission right now. Under an opt-out policy it can be
|
|
226
|
+
`true` before the visitor has chosen anything. Read
|
|
227
|
+
[how consent works](../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice)
|
|
228
|
+
before you record or report choices. For iframes, use
|
|
229
|
+
[`ConsentGate`](./components/consent-gate.md).
|
|
230
|
+
|
|
231
|
+
## Check that it works
|
|
129
232
|
|
|
130
|
-
|
|
131
|
-
|
|
233
|
+
Build and preview the production bundle with `vite build` and `vite preview`,
|
|
234
|
+
then open the site in a private window with DevTools open on the Network tab.
|
|
235
|
+
Test under a policy that asks for a choice, such as an EU opt-in policy.
|
|
132
236
|
|
|
133
|
-
|
|
237
|
+
1. **Before a choice.** The banner shows, with no `/manifest` request. There
|
|
238
|
+
are no requests to `posthog.com`.
|
|
239
|
+
2. **Reject All.** The banner closes and the vendor requests stay absent.
|
|
240
|
+
3. **Reload.** The banner stays closed and the vendor requests stay absent.
|
|
241
|
+
4. **Privacy settings.** The footer link opens the dialog with **Analytics** (the
|
|
242
|
+
`measurement` category) and **Marketing** off. Turn on **Analytics** and
|
|
243
|
+
save. PostHog's `array.js` loads.
|
|
244
|
+
5. **Turn Analytics off again.** The page reloads and PostHog does not load.
|
|
134
245
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
[
|
|
138
|
-
|
|
246
|
+
If the banner never appears, check the generated policy and any location
|
|
247
|
+
request to `/init` on your backend URL. [Troubleshooting](./troubleshooting.md)
|
|
248
|
+
covers the common causes. [Verify consent](../../guides/verify-consent.md) has the
|
|
249
|
+
full release checklist.
|
|
139
250
|
|
|
140
|
-
##
|
|
251
|
+
## Next steps
|
|
141
252
|
|
|
142
|
-
|
|
143
|
-
|
|
253
|
+
* [Rendering](./rendering.md) for server-rendered React apps and
|
|
254
|
+
what the first paint contains.
|
|
255
|
+
* [Scripts and embeds](./scripts.md) for more vendors, network
|
|
256
|
+
blocking and data cleanup.
|
|
257
|
+
* [Customize](./customize.md) for colors, fonts, layout and copy.
|
|
258
|
+
* [Components](./components/consent-provider.md) for
|
|
259
|
+
every provider option and component prop.
|
|
260
|
+
* The runnable app in `examples/react` of the
|
|
261
|
+
[c15t repository](https://github.com/c15t/c15t) contains this setup.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Rendering and deployment
|
|
3
|
+
description: How c15t resolves consent in client-rendered React apps, React
|
|
4
|
+
Router framework mode, Remix and other server-rendered React apps without a
|
|
5
|
+
c15t adapter.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Pick your rendering path
|
|
10
|
+
|
|
11
|
+
`c15t/react` resolves consent in the browser. That works for every React app,
|
|
12
|
+
including server-rendered ones. What changes is what the first HTML contains.
|
|
13
|
+
|
|
14
|
+
| Your app | Where consent resolves | Setup |
|
|
15
|
+
| ------------------------------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| Vite, or another client-rendered single-page app | Browser, after the bundle loads | [Quickstart](./quickstart.md) |
|
|
17
|
+
| Prerendered or static HTML | Browser, after the page loads | [Quickstart](./quickstart.md), see [static pages](#static-and-prerendered-pages) |
|
|
18
|
+
| React Router framework mode, Remix, or another server-rendered React app | Browser, after hydration | [Quickstart](./quickstart.md), see [server-rendered apps](#react-router-remix-and-other-server-rendered-apps) |
|
|
19
|
+
| Next.js or TanStack Start | Server, per request | That framework's guide in the [framework list](https://v3.c15t.com/docs/frameworks) |
|
|
20
|
+
|
|
21
|
+
Pick the Next.js or TanStack Start adapter if you use those frameworks. They
|
|
22
|
+
read the visitor's cookie and location on the server, so the banner can be in
|
|
23
|
+
the first HTML. [Choose your setup](../../concepts/choose-your-setup.md#server-rendering-or-browser-rendering)
|
|
24
|
+
explains the difference in general terms.
|
|
25
|
+
|
|
26
|
+
## Bundle the manifest during Vite builds
|
|
27
|
+
|
|
28
|
+
The [quickstart](./quickstart.md) and `examples/react` in
|
|
29
|
+
the c15t repository use this setup.
|
|
30
|
+
|
|
31
|
+
The build fetches your public policy once and bundles it, so the server never
|
|
32
|
+
fetches it at runtime.
|
|
33
|
+
|
|
34
|
+
Add `consentManifest` to your existing Vite plugin list:
|
|
35
|
+
|
|
36
|
+
```ts title="vite.config.ts (plugins option)"
|
|
37
|
+
import { consentManifest } from 'c15t/build';
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
plugins: [
|
|
41
|
+
// Keep your existing framework plugins here.
|
|
42
|
+
consentManifest({ backendURL: 'https://your-project.inth.app' }),
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Without `backendURL`, the plugin reads `VITE_C15T_BACKEND_URL`, then
|
|
48
|
+
`VITE_INTH_PROJECT_URL`. The plugin
|
|
49
|
+
fetches the policy during `vite build`, or in `vite dev` when the app first
|
|
50
|
+
loads it, and serves it as `snapshot` from `c15t/generated`. A build fetches
|
|
51
|
+
only when the app uses `manifest()`. In `src/consent.tsx`, create the
|
|
52
|
+
provider's mode with `manifest()` from `c15t/react` instead of `hosted()`. It
|
|
53
|
+
reads the snapshot and the backend URL from `c15t/generated`:
|
|
54
|
+
|
|
55
|
+
```tsx title="src/consent.tsx"
|
|
56
|
+
import { manifest } from 'c15t/react';
|
|
57
|
+
|
|
58
|
+
const mode = manifest();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Keep passing `mode` to `ConsentProvider`. The browser resolves the snapshot
|
|
62
|
+
without `/manifest` when it has the geography the policy needs, or the policy
|
|
63
|
+
does not depend on geography. Supply known country and region through `inputs`
|
|
64
|
+
or overrides to keep regional resolution on the snapshot.
|
|
65
|
+
|
|
66
|
+
When the build has no snapshot, as in `vite dev` after a failed fetch,
|
|
67
|
+
`snapshot` is `undefined`, and the browser fetches
|
|
68
|
+
`${backendURL}/manifest` instead.
|
|
69
|
+
|
|
70
|
+
When the required country or region is missing, the transport calls backend
|
|
71
|
+
`/init` and uses its resolved policy and translations. `consentManifest` warns
|
|
72
|
+
during the build when it downloads such a policy, and suggests `hosted()`. That response can use
|
|
73
|
+
newer backend policy than the build snapshot. This is a full consent resolution,
|
|
74
|
+
so these visitors still wait for a backend round trip. `ConsentProvider` sends
|
|
75
|
+
that request while it first renders, before the page mounts, unless the visitor
|
|
76
|
+
has a stored choice.
|
|
77
|
+
|
|
78
|
+
The browser cannot guess the banner from the snapshot alone when locations
|
|
79
|
+
differ. With a European opt-in rule and no banner elsewhere, a banner shown
|
|
80
|
+
before `/init` answers would vanish for most of the world. The exception is a
|
|
81
|
+
policy whose country and region rules all show the same banner, with a default
|
|
82
|
+
rule that covers everywhere else: the browser resolves it at once. To skip the
|
|
83
|
+
round trip for location-specific rules, pass the country your CDN or edge
|
|
84
|
+
already knows through `inputs`.
|
|
85
|
+
|
|
86
|
+
Policy and resolver code are part of the browser bundle.
|
|
87
|
+
|
|
88
|
+
The snapshot is fixed at build time:
|
|
89
|
+
|
|
90
|
+
* Rebuild after changing policies, translations or vendors. If your CI caches
|
|
91
|
+
build output, force a fresh build.
|
|
92
|
+
* Consent choices still go to the backend.
|
|
93
|
+
|
|
94
|
+
The build reads the backend URL from your public backend URL variable when
|
|
95
|
+
the config doesn't pass one: `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js,
|
|
96
|
+
`NUXT_PUBLIC_C15T_BACKEND_URL` in Nuxt, `PUBLIC_C15T_BACKEND_URL` in Astro,
|
|
97
|
+
Svelte and SvelteKit, and `VITE_C15T_BACKEND_URL` in TanStack Start and other
|
|
98
|
+
Vite apps. Each also reads the matching Inth variable, such as
|
|
99
|
+
`NEXT_PUBLIC_INTH_PROJECT_URL`, when the c15t one is unset. See
|
|
100
|
+
[set the backend URL](../../concepts/modes.md#set-the-backend-url).
|
|
101
|
+
|
|
102
|
+
The fetch waits at most 10 seconds. When it fails, or no backend URL is set,
|
|
103
|
+
every framework does the same thing:
|
|
104
|
+
|
|
105
|
+
| Command | Default when the fetch fails |
|
|
106
|
+
| ------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
107
|
+
| Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
|
|
108
|
+
| Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning, and the server fetches the policy at runtime. |
|
|
109
|
+
|
|
110
|
+
Set `onBuildError` to use one behaviour for both. `'fail'` stops dev too.
|
|
111
|
+
`'runtime'` lets a production build finish, and the server fetches the policy
|
|
112
|
+
at runtime. The `C15T_ON_BUILD_ERROR` environment variable overrides the
|
|
113
|
+
option, so you can deploy during a backend outage without a code change:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
C15T_ON_BUILD_ERROR=runtime npm run build
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Turborepo's strict environment mode hides undeclared variables from tasks, so
|
|
120
|
+
list `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
|
|
121
|
+
|
|
122
|
+
The build skips the fetch, without an error, when it can't use a snapshot,
|
|
123
|
+
for example when the backend URL is relative. With `onBuildError: 'fail'`, a
|
|
124
|
+
relative URL stops the build. [Consent modes](../../concepts/modes.md#what-happens-when-the-download-fails)
|
|
125
|
+
lists every case.
|
|
126
|
+
|
|
127
|
+
`vite build` and `vite dev` fetch the manifest when they start. `vite preview`
|
|
128
|
+
serves the last build without fetching. The plugin writes no file into your
|
|
129
|
+
app, so there is nothing to keep out of Git. `c15t/generated` ships its own
|
|
130
|
+
types, so `tsc`, `vue-tsc` and `svelte-check` pass on a fresh checkout without
|
|
131
|
+
a build first.
|
|
132
|
+
|
|
133
|
+
The plugin can't see the options your app passes to `manifest()`. When the
|
|
134
|
+
app passes `source: 'runtime'` or `manifestURL`, set
|
|
135
|
+
`consentManifest({ source: 'runtime' })` too. The build then downloads no
|
|
136
|
+
manifest, so it doesn't fail when the backend's `/manifest` is down.
|
|
137
|
+
|
|
138
|
+
For policy updates without a rebuild, use `hosted({ backendURL })` from `c15t/react`
|
|
139
|
+
as the mode and omit the build plugin. That setup resolves policy through the
|
|
140
|
+
backend `/init` on each page load.
|
|
141
|
+
|
|
142
|
+
## Client-rendered apps
|
|
143
|
+
|
|
144
|
+
The recommended [quickstart](./quickstart.md) bundles the
|
|
145
|
+
manifest and resolves it in the browser. See [build-time manifests](#bundle-the-manifest-during-vite-builds).
|
|
146
|
+
|
|
147
|
+
For policy updates without rebuilding, use `hosted()` from `c15t/react` as
|
|
148
|
+
the mode. It reads the backend URL `consentManifest` read from
|
|
149
|
+
`VITE_C15T_BACKEND_URL` or `VITE_INTH_PROJECT_URL`; without the plugin, pass
|
|
150
|
+
`hosted({ backendURL })`.
|
|
151
|
+
That runtime path works as follows:
|
|
152
|
+
|
|
153
|
+
1. The page loads with no consent state. Every optional category is denied.
|
|
154
|
+
2. `ConsentProvider` requests `GET /init` from your backend URL while it first
|
|
155
|
+
renders, before the page paints. The backend picks the visitor's policy
|
|
156
|
+
from their location. With an `experiment`, the request waits until the
|
|
157
|
+
provider mounts, because it carries the visitor's arm.
|
|
158
|
+
3. The provider applies the policy and any stored choice. It shows the banner
|
|
159
|
+
if the policy asks for one, and loads the scripts the visitor allowed.
|
|
160
|
+
|
|
161
|
+
Until step 3 finishes, gated scripts and `ConsentGate` embeds stay blocked. If
|
|
162
|
+
the backend request fails, they stay blocked and no banner shows.
|
|
163
|
+
|
|
164
|
+
## React Router, Remix and other server-rendered apps
|
|
165
|
+
|
|
166
|
+
React Router framework mode, Remix and similar frameworks have no c15t adapter.
|
|
167
|
+
Use the quickstart's `Consent` component and render it in your root component,
|
|
168
|
+
around the outlet. In React Router framework mode that is `app/root.tsx`.
|
|
169
|
+
|
|
170
|
+
`ConsentProvider` renders on the server without touching browser APIs, and the
|
|
171
|
+
browser takes over after hydration:
|
|
172
|
+
|
|
173
|
+
* The server HTML contains your page but no banner and no consent state.
|
|
174
|
+
* After hydration the browser resolves the bundled manifest, then shows the
|
|
175
|
+
banner if the policy asks for one. When the banner depends on a country or
|
|
176
|
+
region the browser does not know, it calls `/init` and uses the backend's
|
|
177
|
+
current policy and translations, which can differ from the build snapshot.
|
|
178
|
+
With `hosted()`, it always calls `/init`.
|
|
179
|
+
* The server render and the first browser render agree, so hydration does not
|
|
180
|
+
warn or change a stored choice.
|
|
181
|
+
|
|
182
|
+
The Vite setup needs no change. The banner renders in the browser and brings
|
|
183
|
+
its own `<style>` element, so there is no stylesheet to import.
|
|
184
|
+
|
|
185
|
+
### Skip the browser's first init request
|
|
186
|
+
|
|
187
|
+
This is optional. `fetchSSRData` from `c15t/react/server` calls the backend's
|
|
188
|
+
`/init` from your server loader, forwarding the visitor's location and
|
|
189
|
+
language headers. Pass the result to the browser, wrap it in a promise and give
|
|
190
|
+
it to `hosted({ backendURL, initialData })`. The provider's first init then uses that
|
|
191
|
+
response instead of a network request.
|
|
192
|
+
|
|
193
|
+
Know its limits before you add it:
|
|
194
|
+
|
|
195
|
+
* It saves the browser's round trip to the backend. It does not put the banner
|
|
196
|
+
in the server HTML; the banner still mounts after hydration.
|
|
197
|
+
* Every page view makes a server-to-server `/init` request. Do not cache the
|
|
198
|
+
loader response, because it contains one visitor's policy.
|
|
199
|
+
* It returns `undefined` when the request has no location or language headers,
|
|
200
|
+
and the browser then requests `/init` as usual.
|
|
201
|
+
* `mode` is read once, when the provider mounts. Create the `hosted()` mode once
|
|
202
|
+
in the browser rather than on every render.
|
|
203
|
+
* A relative `backendURL` resolves against the request's `host` header, over
|
|
204
|
+
`https` for a domain name and over `http` for `localhost`, an IP address or
|
|
205
|
+
a single-label host. `fetchSSRData` ignores `x-forwarded-host`,
|
|
206
|
+
`x-forwarded-proto` and `forwarded`, and does not send them to the backend.
|
|
207
|
+
Behind a proxy that overwrites those headers, pass
|
|
208
|
+
`trustForwardedHeaders: true` to use them.
|
|
209
|
+
* If your server answers requests for any `Host` header, such as a Node server
|
|
210
|
+
exposed directly or a proxy that forwards `Host` from its default virtual
|
|
211
|
+
host, pass `fetchSSRData` an absolute `backendURL`. A relative one resolves
|
|
212
|
+
against whatever `Host` the request sent.
|
|
213
|
+
|
|
214
|
+
### Render the banner in the server HTML
|
|
215
|
+
|
|
216
|
+
c15t has no framework-neutral helper for this. `ConsentProvider` accepts a
|
|
217
|
+
server-resolved `prefetch` state, but the Next.js and TanStack Start adapters
|
|
218
|
+
build that state from the request's cookie, headers and a cached policy
|
|
219
|
+
manifest. Building it by hand ties your app to internal shapes. If the banner
|
|
220
|
+
must be part of the first paint, use one of those adapters. Otherwise keep the
|
|
221
|
+
browser path.
|
|
222
|
+
|
|
223
|
+
## Static and prerendered pages
|
|
224
|
+
|
|
225
|
+
A prerendered page is the same HTML for every visitor, so it cannot contain one
|
|
226
|
+
visitor's consent. Use the quickstart's build-time snapshot. Consent saves and
|
|
227
|
+
any `/init` requests go to the backend URL directly, so the static host
|
|
228
|
+
needs no server route. Add the site's
|
|
229
|
+
origin to the trusted origins in your Inth project.
|
|
230
|
+
|
|
231
|
+
Do not cache a response that contains a visitor's consent in a CDN or a shared
|
|
232
|
+
page cache. [Data fetching](../../concepts/data-fetching.md) covers what is safe to
|
|
233
|
+
cache.
|
|
234
|
+
|
|
235
|
+
## Where the browser sends requests
|
|
236
|
+
|
|
237
|
+
With `hosted()`, the browser calls two backend endpoints:
|
|
238
|
+
|
|
239
|
+
| Request | When |
|
|
240
|
+
| --------------------- | ------------------------------------------ |
|
|
241
|
+
| `GET {url}/init` | Once per page load, to resolve the policy |
|
|
242
|
+
| `POST {url}/subjects` | When the visitor accepts, rejects or saves |
|
|
243
|
+
|
|
244
|
+
With a bundled manifest, `manifest()` sends the same
|
|
245
|
+
`/subjects` request, and requests `/init` only when the policy depends on a
|
|
246
|
+
country or region the browser does not know.
|
|
247
|
+
|
|
248
|
+
These requests go to the backend's origin, so it must list your site as a
|
|
249
|
+
trusted origin.
|
|
250
|
+
[Data fetching](../../concepts/data-fetching.md) compares this with proxies and
|
|
251
|
+
custom transports.
|
|
252
|
+
|
|
253
|
+
## Check the rendering path
|
|
254
|
+
|
|
255
|
+
1. Open the page in a private window and view the page source. In a
|
|
256
|
+
server-rendered app the HTML contains your content and no element with
|
|
257
|
+
`data-testid="consent-banner-root"`.
|
|
258
|
+
2. In DevTools Network, confirm there is no `/manifest` request with a
|
|
259
|
+
build-time snapshot. A regional policy may still need an `/init` request
|
|
260
|
+
to resolve consent when geography is missing. With `hosted()`, one `/init`
|
|
261
|
+
request runs unless you supply `initialData`.
|
|
262
|
+
3. The banner appears once the policy resolves. Vendor requests stay absent
|
|
263
|
+
until you allow their category.
|