From 9b3b82882e950295ee7d2eefb50e3314d5c650e6 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Wed, 18 Mar 2026 02:41:43 +0000 Subject: [PATCH] feat: add Core Web Vitals reporting to PostHog Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/lib/webVitals.ts | 35 +++++++++++++++++++++++++++++++++++ frontend/src/main.tsx | 4 ++++ 2 files changed, 39 insertions(+) create mode 100644 frontend/src/lib/webVitals.ts diff --git a/frontend/src/lib/webVitals.ts b/frontend/src/lib/webVitals.ts new file mode 100644 index 00000000..d4427b0b --- /dev/null +++ b/frontend/src/lib/webVitals.ts @@ -0,0 +1,35 @@ +/** + * Core Web Vitals reporting via PostHog. + * + * Tracks LCP, INP, CLS, FCP, and TTFB as PostHog events so we can + * build dashboards and correlate performance with product usage. + * + * Each metric fires once per page load. PostHog captures 100% of + * sessions (vs Sentry's 20% sample), giving better coverage. + */ +import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals' +import type { Metric } from 'web-vitals' +import posthog from 'posthog-js' + +function sendToPostHog(metric: Metric) { + // Only send if PostHog is loaded + if (!(posthog as unknown as { __loaded?: boolean }).__loaded) return + + posthog.capture('web_vitals', { + metric_name: metric.name, + metric_value: metric.value, + metric_rating: metric.rating, + metric_delta: metric.delta, + metric_id: metric.id, + page_path: window.location.pathname, + }) +} + +/** Register all Web Vitals observers. Call once after PostHog init. */ +export function initWebVitals() { + onLCP(sendToPostHog) + onINP(sendToPostHog) + onCLS(sendToPostHog) + onFCP(sendToPostHog) + onTTFB(sendToPostHog) +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 28272af2..cb21d7c9 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -6,6 +6,7 @@ import { createRoot } from 'react-dom/client' import { reactErrorHandler } from '@sentry/react' import { HelmetProvider } from 'react-helmet-async' import { PostHogProvider } from '@posthog/react' +import { initWebVitals } from './lib/webVitals' import { Toaster } from 'sonner' import './index.css' import App from './App' @@ -22,6 +23,9 @@ if (posthogKey) { }) } +// Start Web Vitals reporting to PostHog +initWebVitals() + createRoot(document.getElementById('root')!, { onUncaughtError: reactErrorHandler(), onCaughtError: reactErrorHandler(),