web-vitals

web-vitals: Measure Core Web Vitals in the Browser

Library
npm
v6.2.2
8,626 stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
75 /100 Good
Development Activity 84
Maintenance 48
Community 68
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
84 /100 Excellent
Architecture 84
Code Quality 88
Innovation 82
Learning Curve 80

web-vitals is a tiny (~2KB brotli’d), modular library from the Chrome team for measuring the Core Web Vitals — Cumulative Layout Shift (CLS), Interaction to Next Paint (INP), and Largest Contentful Paint (LCP) — plus supporting metrics like First Contentful Paint (FCP) and Time to First Byte (TTFB), on real users in production. Crucially, it calculates each metric the exact same way Chrome does internally, so the numbers match what shows up in the Chrome User Experience Report, PageSpeed Insights, and Search Console’s Speed Report.

Each metric is exposed as a single function (onCLS, onINP, onLCP, onFCP, onTTFB) that takes a callback fired once the value is ready to report, with support for reporting on every change, reporting only deltas, and batching multiple metric reports together before sending them to an analytics endpoint. An optional “attribution” build adds diagnostic detail (e.g. which DOM element caused the layout shift) to help pinpoint the root cause of a poor score.

What You Get

  • onCLS, onINP, onLCP, onFCP, onTTFB - one function per metric, each accepting a callback fired when the value is ready to report
  • A “standard” build (~2KB) and an “attribution” build (+1.5KB) that adds root-cause diagnostic data to each metric
  • reportAllChanges option to observe a metric’s value as it changes in real time, useful for debugging
  • Delta-based reporting via each metric’s id/delta fields, for analytics providers that can’t overwrite previously-sent values
  • Built-in handling of edge cases: back/forward cache restores, page visibility changes, and pages loaded in the background

Common Use Cases

  • Sending real-user Core Web Vitals data to Google Analytics or Google Tag Manager to track site performance over time
  • Piping metrics to a custom analytics/RUM backend via navigator.sendBeacon or fetch for internal performance dashboards
  • Debugging a specific poor Core Web Vitals score using the attribution build to identify the exact element or resource responsible
  • Correlating field (real-user) performance data against lab-based Lighthouse scores to validate synthetic testing results

Under The Hood

Architecture Each metric has a dedicated entry file (src/onCLS.ts, src/onINP.ts, src/onLCP.ts, src/onFCP.ts, src/onTTFB.ts) that wires a PerformanceObserver (via the shared src/lib/observe.ts helper) to metric-specific accumulation logic — e.g. LayoutShiftManager.ts sums layout-shift session windows for CLS, InteractionManager.ts and LCPEntryManager.ts track interaction/paint timing for INP and LCP — then reports through a shared bindReporter.ts callback wrapper that handles the report-once vs report-all-changes vs delta-reporting behavior uniformly across metrics. Tech Stack Modular TypeScript (36% of source) compiled to ESM/CJS/IIFE bundles via Rollup, published with an exports map allowing consumers to import individual metric functions (tree-shakeable) or the full web-vitals or web-vitals/attribution entry points; there are no runtime dependencies. Code Quality The test/ directory pairs unit tests with WebdriverIO-driven browser integration tests (wdio.conf.js) that exercise real Core Web Vitals behavior across actual browser sessions rather than mocked APIs, reflecting the project’s status as the reference implementation Chrome itself validates other tools against; ESLint (flat config) enforces consistent style. API Design The one-function-per-metric design (onCLS(callback), onLCP(callback), etc.) is deliberately minimal — a single import and callback gets a working integration, and the standard vs attribution build split lets consumers opt into diagnostic complexity only when they need root-cause data, keeping the default path lightweight.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers