Measuring and Improving Frontend Performance: A Core Web Vitals Playbook
You get a ticket: "Make the website faster." No specific metric. No target. Just "faster."
15 Oct 2023

Frontend performance is not a vibe, it is a set of metrics Google publishes and users feel. If you cannot name what you are measuring and what "good" is, you are guessing. This is the playbook: the three metrics that matter, how to measure them honestly, and the highest-leverage fixes for each.
The three Core Web Vitals
Google's Core Web Vitals are the numbers that map to real user experience, and they gate search ranking. A page passes only when at least 75% of real-user visits hit "good" on all three at once:
- Largest Contentful Paint (LCP) measures loading. Good is under 2.5 seconds. It marks when the biggest element (usually a hero image or heading) becomes visible.
- Interaction to Next Paint (INP) measures responsiveness. Good is under 200 milliseconds. In March 2024 it replaced First Input Delay.
- Cumulative Layout Shift (CLS) measures visual stability. Good is under 0.1. It quantifies how much content jumps around as the page loads.
INP is the one most sites fail: roughly 43% of the web is over the 200ms threshold. It is worth understanding why.
Why INP replaced FID (and why it is harder)
First Input Delay only measured the delay before the browser could begin processing the first interaction. A single data point, at the start of the page, that told you almost nothing. INP measures every interaction (clicks, taps, key presses) across the full page lifecycle and reports roughly the worst one at the 75th percentile.
That makes it far harder to game and far more representative. A page that felt fine under FID can fail INP badly, because the expensive interaction was the third click, not the first.
Measuring honestly: lab vs field
There are two kinds of data and they answer different questions.
- Lab data (Lighthouse, WebPageTest, the Performance panel) runs in a controlled environment. Great for debugging and catching regressions in CI, but it is a simulation.
- Field data (Chrome UX Report, the
web-vitalslibrary, your RUM tool) is what real users on real devices experienced. This is what Google ranks on, so this is the number that counts.
Collect field data from real sessions:
import { onLCP, onINP, onCLS } from "web-vitals";
function report(metric: { name: string; value: number }) {
navigator.sendBeacon("/vitals", JSON.stringify(metric));
}
onLCP(report);
onINP(report);
onCLS(report);
Optimize against the 75th percentile of this, not against a green Lighthouse score on your fast laptop.
Fixing LCP
LCP is usually a loading problem for one specific element:
- Serve the hero image in a modern format (
AVIF/WebP) at the right size, and give itfetchpriority="high". - Preload the LCP resource and avoid lazy-loading anything above the fold.
- Cut render-blocking CSS and slow server response (TTFB). A CDN and streaming SSR help.
<link rel="preload" as="image" href="/hero.avif" fetchpriority="high" />
<img src="/hero.avif" width="1200" height="630" alt="..." />
Fixing INP
INP is about keeping the main thread free to respond:
- Break up long tasks. Yield to the browser (
await scheduler.yield()or chunk work) so a single handler does not block for 200ms. - Move heavy computation off the main thread with a Web Worker.
- Debounce and throttle high-frequency handlers, and defer non-urgent state updates with
startTransition. - Ship less JavaScript. The cheapest interaction is the one that runs against a smaller bundle.
Fixing CLS
CLS is almost always unreserved space:
- Set explicit
widthandheight(oraspect-ratio) on images and video so the browser reserves the box. - Reserve space for ads, embeds, and late-loading banners.
- Use
font-display: optionalor preload fonts so text does not reflow when the web font swaps in.
Wrapping up
Performance work without measurement is superstition. Learn the three numbers, LCP under 2.5s, INP under 200ms, CLS under 0.1, measure them from real users at the 75th percentile, and attack whichever one is failing with the fixes above. INP is the modern battleground, and it is won by shipping less JavaScript and keeping the main thread free.
From Senior to Staff: Master the Architecture Skills That Get You Promoted
Go from shaky in design reviews to the engineer everyone trusts to architect the hard stuff.
View the live cohortKeep reading
- React 19 Performance Secrets, Concurrent Rendering, Suspense & Real Tips
- Lazy Loading in HTML and CSS: Enhancing Performance and User Experience
- Edge Rendering with Next.js and Cloudflare Workers
- Rolling Out an Internal UI Component Library
- What is a Design System According to Atlassian?
- CSS Houdini: New Possibilities for Your CSS