Skip to main content

Frontend Tracking

For user and UX analytics, the frontend sends events through a single GraphQL mutation, and the shared hook useFrontendTracking handles payload shaping and deduping.

How it works

This is the shortest mental model that stays correct as you add new tracking:

1) Call useFrontendTracking() in your component

Example:

2) The hook dedupes events locally

useFrontendTracking builds a dedupe key and keeps a bounded cache in a Map so repeated events within a short window are ignored. That logic lives entirely in: apps/portal/src/components/hooks/useFrontendTracking.ts

3) The mutation contract stays stable

Events are sent using the generated mutation hook: useTrackFrontendEventMutation The mutation document is: apps/portal/src/gql/mutations/trackFrontendEvent.gql.ts Tracking honors consent before any event is sent. useFrontendTracking checks gdpr_consent through getGdprConsent() and treats unknown consent as denied. When consent is denied, the hook skips trackFrontendEvent entirely.
📖 See also: GDPR Consent Flow for frontend-to-backend enforcement boundaries.

Extending this system

Use this checklist when you add new UX tracking from components.
  1. Import and initialize the tracking hook in your component:
Example:
  1. Call track(payload) with:
    • eventDomain: one of "auth" | "billing" | "navigation"
    • eventName: your specific action string
    • optional properties: a string-to-string map
  2. If you need a new eventDomain, update:
    • FrontendTrackingDomainType in apps/portal/src/components/hooks/useFrontendTracking.ts
    • the mapping to FrontendEventDomainEnum in the same file
  3. Verify:

What not to do

These anti-patterns break dedupe or reintroduce fragmented analytics paths. ❌ Never send analytics SDK calls directly from the browser. ❌ Never send high-cardinality data as tag-like strings; prefer bucketing on the backend analytics pipeline. ❌ Never bypass the dedupe helper by reimplementing it in each component.

File conventions

This is where tracking code should live so it stays easy to audit. Prefer:
  • small, named events (eventName) instead of free-form long strings
  • short properties only when they help debug or understand funnels

What’s next?

If the tracked UX involves forms, follow the forms and error pages next. If you’re adding forms that trigger these events, use the forms and error UX pages next.