The Core Web Vitals
Google's Core Web Vitals, the three field metrics that score real-user performance and feed Search ranking.
Your app is in production and Speed Insights is streaming numbers from real users’ phones. But a red LCP pill is just noise until you know what LCP measures, why Google scores it, and what usually fixes it.
This lesson builds that model for the three numbers Google grades you on, so one metric tells you what is wrong and which lesson in this chapter fixes it.
The three Core Web Vitals and how they’re scored
Section titled “The three Core Web Vitals and how they’re scored”Google grades three numbers that predict whether a user is having a good time, and they feed Google Search ranking.
- LCP : how long until the main thing on the page appears.
- INP : when you tap something, how long until the page responds.
- CLS : how much the page jumps around while you’re trying to read it.
Each names a frustration you already know: the page that shows nothing, the button that ignores your press, the article that shoves itself down as you read. Each gets its own section below; first, the rule that grades all three.
The 75th-percentile rule
Section titled “The 75th-percentile rule”Vitals are scored at the 75th percentile (p75) of your production traffic, not the average: 75% of users get at least this experience, and the other 25% get something worse.
Why p75? Because averages hide the tail. If half your users load in half a second and half wait four seconds on a weak signal, the average is a comfortable 2.25 seconds, and a lie: a quarter of your users are miserable. The p75 won’t let you average that pain away; it points at the user near the back of the line.
This answers “but it’s fast on my machine.” You have a fast laptop, fast Wi-Fi, and a warm cache; Google scores the mid-range phone on a flaky network.
A trailing window, not a live alarm
Section titled “A trailing window, not a live alarm”The public score Google Search uses comes from CrUX (the Chrome User Experience Report), a rolling 28-day window of real Chrome-user field data; Speed Insights mirrors a recent-window p75 of your own traffic. Either way the figure is a trend over weeks, not the last hour, so a regression you ship today won’t move the public score for a week or two.
The three thresholds
Section titled “The three thresholds”Each Vital is graded good, needs improvement, or poor, and you want all three green at p75. The “good” thresholds are the only numbers in this lesson worth memorizing:
- LCP: good at ≤ 2.5 seconds (poor above 4.0s).
- INP: good at ≤ 200 milliseconds (poor above 500ms).
- CLS: good at ≤ 0.1 (poor above 0.25).
CLS is unitless, a score rather than a time, which makes more sense once you’ve seen how it’s calculated. Speed Insights plots all three against these bands, so a glance shows which need work.
The sections below add, for each Vital, what it really measures, the one cause that dominates it, and the structural change that moves it.
LCP: when the largest element paints
Section titled “LCP: when the largest element paints”LCP measures the render time of the largest content element visible in the initial viewport, usually the hero image, a big headline, or the first card in a list, the thing the page is about. In short: how long until the main thing shows up.
LCP tracks one element, not the whole page. Not “fully loaded,” not when the spinner stops or the last analytics script finishes, just the moment that single biggest element paints. On a marketing page that’s almost always the hero image, as in the figure below.
Why the network owns LCP
Section titled “Why the network owns LCP”Walk the LCP element through the render pipeline you already know. Four things happen in order: the HTML arrives, the browser discovers the element inside it, fetches its bytes, and paints it.
The slow step is almost always byte delivery. A hero image is hundreds of kilobytes traveling down a real, often mobile, connection, and a headline in a custom font can’t paint until the font file arrives. Either way the bottleneck is the critical path , a network problem, not a CPU one.
What goes wrong, and how you fix it
Section titled “What goes wrong, and how you fix it”In a Next.js web app, a poor LCP almost always traces to one of three things, each with its own fix:
- The hero image shipped without a priority hint, so the browser discovers and fetches it too late. Mark it high priority so the browser fetches it immediately; that’s the next lesson.
- The headline waits on a custom font not loaded through
next/font. Load the typeface throughnext/fontand it ships with the page instead of being discovered late. - A Server Component awaits slow upstream data, like a database query, before it can render the element. Get that fetch off the critical path so it doesn’t block first paint; that’s the Server Component waterfalls lesson later in this chapter.
LCP has three levers where the other two Vitals have one, because it sits exactly where the network, your fonts, and your server timing meet.
INP: how fast the page responds to taps
Section titled “INP: how fast the page responds to taps”INP measures interaction latency across the whole visit: from the moment you tap, click, or press a key, to the next frame the browser paints in response.
It reports not your average interaction but roughly the worst, technically near the p98 of the page’s interactions. A page can feel snappy for nineteen taps and still earn a poor INP from the twentieth, so one heavy handler can quietly drag down the score.
Why the main thread is the bottleneck
Section titled “Why the main thread is the bottleneck”The browser runs your JavaScript and paints the screen on one and the same thread, the main thread , and the two take turns.
So when you tap a button, the browser wants to run your click handler and then paint, but if the thread is already busy, your tap waits in line.
And it gets busy easily: a heavy Client Component re-rendering its subtree, a synchronous JSON.parse chewing through a large payload, a third-party analytics script hogging the event loop.
While any of that runs, the thread can’t process input or paint, and the page looks frozen because for that instant it is.
INP replaced FID
Section titled “INP replaced FID”Until March 2024, the interactivity Vital was FID (First Input Delay), which measured only the first interaction and only the wait before its handler started, not how long the handler or paint took. INP closes both gaps: it measures every interaction and includes processing and rendering time, so it reflects what the user actually feels. If a tutorial or dashboard still talks about FID, it’s stale; the metric you care about is INP.
How you fix it
Section titled “How you fix it”The structural fix for INP is to ship less JavaScript to the client.
Every line of client JavaScript can land on the main thread and block a tap.
A Server Component does its work on the server and sends down finished HTML at no client cost, so let the server do the work and put 'use client' only on the genuine interactive leaves of the tree, not high up where it drags everything below onto the client.
When a single widget is unavoidably heavy, you have further moves: code-splitting it with dynamic(), debouncing a high-frequency handler, or pushing heavy synchronous work to a Web Worker.
The next two lessons cover finding and cutting client JavaScript weight, the real lever here.
To see which interaction is slow and why, the Chrome DevTools Performance panel has an INP overlay that points straight at the offending interaction.
CLS: how much the page jumps while you read
Section titled “CLS: how much the page jumps while you read”CLS scores unexpected layout shifts over the page’s lifetime: content that moves after it was already painted.
Picture opening an article and reaching to tap a button. In the half-second before your finger lands, an image loads above the button, the page shoves down, and you tap an ad instead. That jump is a layout shift, and it’s what CLS scores. The “unexpected” part matters: a shift you triggered by tapping “show more” doesn’t count, but one that happens to you while you read does.
How the score adds up
Section titled “How the score adds up”You’ll never compute CLS by hand, so hold the intuition, not the arithmetic. Each shift multiplies two factors: how much of the screen moved (the impact) and how far it moved (the distance). A small element nudging a few pixels barely registers; a big block jumping half the screen scores badly. The page sums its worst shift windows, so one big jump and a steady drip of little ones both count against you.
What causes it, and how you fix it
Section titled “What causes it, and how you fix it”Layout shifts almost always trace to one mistake: content that arrives without its space reserved.
- An image with no declared dimensions. The browser leaves a zero-height box, then expands it when the bytes arrive, pushing everything below down.
- A banner, toast, or cookie bar injected late, shoving aside content already there.
- A font swap. The fallback text paints, then the web font loads at a different size and the text reflows .
The fix is one idea: reserve the space for everything dynamic before it arrives.
next/imagerequireswidthandheight(or a sizedfillcontainer). The reservation it forces is the CLS fix; the blur placeholder you might add is UX polish, not the fix. (More next lesson.)next/fontships fallback metrics tuned so the swap to the web font doesn’t change the text’s size, so nothing reflows.- A loading skeleton must match the real content’s dimensions, or you’ve just moved the shift to the moment it’s replaced.
- Modals and toasts should overlay with
position: fixedso they sit on top of the flow instead of displacing it.
The diagram contrasts the two directly: the same image load, with and without a pre-sized box.
Why it’s not cosmetic
Section titled “Why it’s not cosmetic”CLS isn’t harmless. A page that jumps mis-fires clicks: the user reaches for “Buy” and hits “Cancel,” or taps an ad they never wanted, losing you revenue and trust in one gesture. A page that visibly lurches also reads as broken and amateur.
Of the three Vitals, CLS is the cheapest to fix, since it mostly means declaring dimensions you should have declared anyway, and the most conspicuous to leave broken.
Field data scores you, lab data catches regressions
Section titled “Field data scores you, lab data catches regressions”Vitals are measured two ways, and confusing them is the classic beginner mistake.
Field data is what Speed Insights shows: real users on real devices and networks, aggregated into the 28-day p75. It’s what Google Search scores you on, the verdict on whether users are suffering.
Lab data comes from a tool like Lighthouse: one synthetic run with a throttled network, a mid-tier mobile CPU, no extensions, no cache. Reproducible, instant, and runnable before you deploy, it catches regressions but is never the verdict.
The rule:
Chase the field numbers. Use the lab numbers to stop regressions before they ship.
The trap: a beginner finds Lighthouse, sees a score out of 100, and chases a perfect 100 for one synthetic profile, tuning for a simulated phone no one owns while real users on flaky networks keep hurting. The synthetic 100 is a vanity metric; the field p75 is your users.
Lock in the distinction with the exercise below.
Each chip describes a job. Drop it under the surface that does it. Drag each item into the bucket it belongs to, then press Check.
TTFB and FCP: where to look when LCP is slow
Section titled “TTFB and FCP: where to look when LCP is slow”Speed Insights reports two more numbers beside the three Vitals. Neither is a Core Web Vital, so treat them not as goals but as clues for where to look when LCP goes red.
TTFB is how long the server takes to send the first byte of HTML after the request goes out. A high TTFB, often a stack of sequential awaits or a slow query, delays everything downstream before the browser sees a single byte.
FCP is when any content first paints: the first text or image, not necessarily the big one. Read it against the others:
- A high FCP means something blocked the browser from painting early, usually a render-blocking stylesheet or script, or a slow TTFB.
- An on-time FCP with a much later LCP means painting started fine and the holdup is the LCP element itself, its image or font.
So whenever LCP is red, read in order: TTFB (slow server?), FCP (rendering started late?), then the LCP element (just that one image?).
The two cadences of performance vigilance
Section titled “The two cadences of performance vigilance”The 28-day lag tells you when performance work has to happen. By the time a regression visibly moves the field score, it has been live and hurting users for a week or two. So you cannot catch regressions in the field; you catch them in the lab, with a check that runs before the deploy, the pre-deploy gate of the Lighthouse lesson coming up.
That gives the chapter its two rhythms of performance vigilance:
- A pre-launch deep pass, done once: before you ship, audit your highest-traffic pages, find what’s slow, and fix it structurally.
- Recurring vigilance: the lab gate on every pull request, plus a weekly glance at your slowest database queries. Small, repeated checks that keep the app fast as it grows.
That is the thesis in one line: a few recurring checks, plus a handful of structural defaults you set once and never undo.
You can now look at any Vital and name what’s wrong. Here is where each fix lives in the lessons ahead.
Next we make the LCP image fast.
External resources
Section titled “External resources”The thresholds in this lesson are Google’s, and Google has tightened them before, so the canonical place to confirm the current numbers is web.dev’s own pages.
Google's canonical definitions of LCP, INP, and CLS with the current good thresholds — the source of truth to re-check if the numbers ever drift.
The authoritative statement of what INP measures and why it replaced FID in March 2024.
Vercel's own walkthrough of the triad and the field-vs-lab split, wired to the exact Speed Insights stack this chapter uses.
Paste any URL and watch its layout shifts animate frame by frame — an interactive way to see CLS instead of just reading about it.