How to Measure React Web Vitals and Rendering Performance

Measure Core Web Vitals with the web-vitals library and React render time with the Profiler, then keep field data separate from lab data.

6 min read

React performance has two sides that are easy to confuse. Web Vitals measure the experience real users feel, such as how fast the page loads and how quickly it responds.

Render performance measures how long React itself spends producing the UI. Each needs its own tool.

Fixing one without the other leaves the page slow in ways your tools never showed you.

Core Web Vitals at a glance

Core Web Vitals are three user-facing metrics that apply to every page.

MetricWhat it measuresGood threshold
LCPLoading speed2.5 seconds or less
INPInteraction responsiveness200 ms or less
CLSVisual stability0.1 or less

LCP is Largest Contentful Paint, the time until the largest visible element renders. INP is Interaction to Next Paint, which replaced First Input Delay in 2024 and covers every interaction, not just the first. CLS is Cumulative Layout Shift, the amount the page jumps while loading.

A page passes when it meets all three targets at the 75th percentile across real visits, not on a single fast load. FCP, TTFB, and TBT support diagnosis but are not Core Web Vitals. FCP and TTFB help trace slow LCP, while TBT is a lab-only proxy for INP because simulated page loads have no real user input.

Measure Web Vitals with the web-vitals library

The web-vitals package wraps the browser's Performance APIs so the numbers match what Chrome reports to Google tools. Install it as a dependency.

bashbash
npm install web-vitals

Then report each metric as it becomes available. The library exposes one function per metric, and each calls your callback with a metric object.

index.jsindex.js
import { onCLS, onINP, onLCP } from "web-vitals";
 
function sendToAnalytics(metric) {
  const body = JSON.stringify(metric);
  navigator.sendBeacon("/analytics", body);
}
 
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);

The callback receives a metric with a name, value, rating, and id. sendBeacon delivers the data even while the page is unloading. This is field measurement, because it runs on real users' devices.

Some metrics report only after an interaction or tab switch, so INP never fires for a page the user only looked at. For single-page apps, pass reportSoftNavs true to capture metrics per client-side route change in Chromium browsers that support it.

Measure render time with the Profiler

Web Vitals tell you the page is slow, but not which component is responsible. The React Profiler answers that by recording how long each component takes to render.

App.jsxApp.jsx
import { Profiler } from "react";
 
function onRender(id, phase, actualDuration) {
  console.log(`${id} ${phase}: ${actualDuration.toFixed(2)}ms`);
}
 
export default function App() {
  return (
    <Profiler id="App" onRender={onRender}>
      <ProductList />
    </Profiler>
  );
}

The onRender callback fires on every commit with the id, the phase, and actualDuration. The interactive Profiler tab in React DevTools exposes the same data as a flamegraph, which the Profiler workflow walks through in detail.

actualDuration is the number to watch: it shows how long the subtree really took, while baseDuration estimates the cost without memoization, so the gap between them reveals how much headroom exists.

Field data versus lab data

Field data comes from real users and reflects their devices, networks, and interactions. Lab data comes from your machine or a simulated Lighthouse run. The two disagree often, because a development laptop is far faster than the average phone.

The same code can score differently on a slow phone over 3G than on your machine, which is why the web-vitals library reports from the browser rather than from a build step.

Trust field data for decisions about what to fix. Use lab data to reproduce and verify a fix locally. Record the same interaction in the Profiler on the failing page, and the largest render is usually the fix target.

When a metric fails in the field, open the corresponding React path, such as avoiding expensive calculations, and follow the measure-first workflow to narrow the cause.

Rune AI

Rune AI

Key Insights

  • Core Web Vitals are LCP, INP, and CLS.
  • Use the web-vitals library to report real-user metrics.
  • The Profiler measures React render time.
  • Field data reflects real users; lab data reflects your machine.
  • Optimize the metric that is actually failing.
RunePowered by Rune AI

Frequently Asked Questions

What are the three Core Web Vitals?

Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). They measure loading, interactivity, and visual stability.

Is INP the new FID?

Yes. INP became a stable Core Web Vital in 2024 and replaced First Input Delay, because it captures the full interaction delay rather than only the first input.

Conclusion

Measure user-facing Web Vitals in the field with the web-vitals library, and measure React render time with the Profiler. Keep the two separate, and act on field data first, since lab machines rarely match real users.