Analytics
Wire one or more analytics providers through config.analytics, gate the cookie-dropping ones behind consent, and dispatch events with useAnalytics().
config.analytics (in @repo/config) enables web analytics: provider scripts render client-side, plus a small server seam (@repo/analytics) for webhook-driven events. A provider sub-key turns it on - there is no enabled flag, and a generated project ships no config.analytics key.
Built-in events
Pageviews and attribution
first_touch cookie.Google Ads conversions
Providers
The two tiers differ only by GDPR cookie use: consent-required providers wait for the cookie banner, privacy-focused ones load immediately.
| Provider | Key | Config value | Consent required? |
|---|---|---|---|
| Google Analytics 4 | google | { measurementId, ads? } | Yes |
| PostHog | posthog | { publicKey, host, uiHost? } | Yes |
| Umami | umami | { websiteId, host } | No |
| Plausible | plausible | { domain?, host? } | No |
| OpenPanel | openpanel | { clientId, trackScreenViews? } | No |
| DataFast | datafast | { websiteId, domain } | No |
| Ahrefs | ahrefs | { key } | No |
| Vercel Analytics | vercel | true | No |
vercelis a boolean; every other provider takes an object whose keys are all required unless marked?.vercelSpeedInsights: trueadditionally mounts Vercel Speed Insights (Web Vitals only) - no cookies, no identity, no consent gate.enableInDev(defaultfalse) loads scripts in local development; flip it on only while wiring a provider.
All analytics keys are public (publishable) and ship to the browser by design. Never paste a secret API key into config.
PostHog analytics and PostHog error reporting are separate clients - the errorTracking instance never waits for consent, so a declining visitor's crashes still reach you.
Tracking calls
One useAnalytics() hook fans every dispatch out to every enabled provider, consent-required ones only once consent is granted.
const { track, trackRevenue, identify, reset } = useAnalytics();
track("signup_completed", { plan: "pro" }); // custom event → all providers
trackRevenue(29, "USD", { orderId: "ord_123" }); // revenue event → all providers
identify(userId, { email }); // user → consent-aware subset (see below)
reset(); // clear identity → called automatically on sign-out| Member | Signature | Reaches |
|---|---|---|
track | (event, properties?) | Every enabled provider |
trackRevenue | (amount, currency, options?) | Every enabled provider; options takes orderId, email, and any extra event properties |
identify | (userId, traits?) | Umami, OpenPanel, DataFast, GA, PostHog; traits and attribution wait for consent when a banner is required |
reset | () | Providers that store an identity (GA, PostHog, OpenPanel) - clears it |
isEnabled / needsConsentBanner / hasConsent | flags | Read-only state for gating your own UI |
grantConsent / revokeConsent | () | Drive the consent decision from your own controls |
- Client-side only - every function no-ops on the server and when no provider is enabled.
identifyfires automatically for authenticated users,reseton sign-out or expiry; call them yourself only for custom flows.- GA receives the pseudonymous
user_idonly - Google prohibits PII, soidentifytraits are dropped there. trackRevenueauto-fillsemailfrom the session for DataFast and fires automatically on the return from a checkout.
Cookie consent
Gate cookie-dropping analytics, chat, and affiliate scripts behind a GDPR consent banner with config.cookieBanner and config.consentPolicy.
Built-in events
The complete catalog of analytics events the boilerplate fires on its own - client events from useAnalytics() and server events from payment webhooks, credit settlement, and auth hooks - with the properties each carries.