Integrations
Wire third-party analytics, support chat, and affiliate trackers behind a GDPR-aware cookie banner.
Third-party services are configured in @repo/config (packages/config/src/index.ts) and rendered entirely client-side - there is no backend package. Each provider turns on by the presence of its config sub-key; only consent-required scripts wait for the banner.
What this covers
Cookie consent
config.cookieBanner tri-state gate (true / false / "auto") that holds consent-required scripts.Analytics
config.analytics across two tiers - consent-required (Google, PostHog) and privacy-focused (Umami, Plausible, and more).Support chat
config.support live-chat widget, wired to Crisp; off until you set config.support.crisp.Affiliate tracking
config.affiliate referral pixels - Refgrow, Affonso, PromoteKit - off until you add a sub-key.Consent vs. surfacing
Two distinct behaviors - don't conflate them. A provider can surface the banner (because it drops cookies) yet still load its script regardless of the visitor's choice. Only Google and PostHog actually wait for consent before tracking.
| Integration | Config key | Drops cookies | Surfaces banner | Waits for consent |
|---|---|---|---|---|
| Google Analytics | config.analytics.google | Yes | Yes | Yes |
| PostHog | config.analytics.posthog | Yes | Yes | Yes |
| Privacy-focused analytics | config.analytics.{umami,plausible,openpanel,datafast,ahrefs,vercel} | No | No | No |
| Crisp chat | config.support.crisp | Yes | Yes | No - loads anyway |
| Affiliate trackers | config.affiliate.{refgrow,affonso,promotekit} | Yes | Yes | No - pixels load anyway |
Under the default cookieBanner: "auto", the banner appears only when a surfacing provider is enabled and the visitor is in a GDPR country (isGdprCountry from @repo/utils/helpers, which fails safe to true when geo is unknown). Out of the box no surfacing provider is enabled, so the banner stays hidden until you enable one (Crisp, a consent-required analytics provider, or an affiliate tracker).
All analytics and affiliate keys are public (publishable) and ship to the browser. Never paste a secret API key into config.
Frequently asked questions
Which integrations actually gate on consent? Only Google Analytics and PostHog hold their dispatch until consent. Crisp and the three affiliate pixels surface the banner (they drop cookies) but load regardless of the choice. Privacy-focused analytics (Umami, Plausible, OpenPanel, DataFast, Ahrefs, Vercel) never trigger the banner.
If I enable Crisp, why does the chat bubble appear before I accept cookies?
Crisp surfaces the banner under "auto" because it drops cookies, but the widget is not held back - it loads as soon as config.support.crisp is set. Affiliate pixels behave the same way.
What does declining do?
The choice persists in a tri-state acceptedCookies cookie. "false" keeps Google/PostHog tracking off; Crisp and affiliate pixels still load (their presence only surfaced the banner).
Do these run during local development?
No. Each group has its own enableInDev flag (config.analytics.enableInDev, config.support.enableInDev, config.affiliate.enableInDev), all defaulting to false. The flag also gates the "auto" banner: a provider counts toward the trigger only when its script would actually load.