Skip to content
Platform internals & deliveryLesson 2 of 4Intermediate15 min

Core Web Vitals

Core Web Vitals score real user experience: LCP for loading, INP for responsiveness, CLS for stability. Learn the thresholds and what moves each one.

Kept on this device only.

In one line: Core Web Vitals reduce "is this page fast?" to three measurable user experiences — how quickly the main content loads (LCP), how responsive it feels to input (INP), and how steady it stays under your eyes (CLS) — each with a Google-defined good/needs-improvement/poor threshold.

What it is

Core Web Vitals are three field metrics Google standardized to capture how a page actually feels to a real person:

  • LCP — Largest Contentful Paint measures loading: how long until the largest content element (usually the hero image or headline) finishes rendering.
  • INP — Interaction to Next Paint measures responsiveness: the latency of the worst interaction across the whole visit, from input to the next paint.
  • CLS — Cumulative Layout Shift measures visual stability: a unitless score for how much visible content jumps around unexpectedly.

Together they cover the three things a user notices first — did it show up, did it answer me, and did it hold still — without drowning you in dozens of lab numbers.

Why it matters

These metrics matter because they are scored from real users, not a clean lab machine. A page that loads in 1.5s on your laptop can land in the "poor" band for a user on a mid-tier phone over flaky mobile data, and the field number is the one that counts. Slow loading, janky input, and shifting layouts all push people to bounce, and the research consistently ties them to lost conversions.

They are also an SEO signal: Core Web Vitals feed Google's page-experience ranking. The distinction to keep straight is field data vs lab data. Field (RUM) data — what real visitors experienced, aggregated at the 75th percentile — is what Google scores and what reflects reality. Lab data — a single synthetic run in a tool like Lighthouse — is reproducible and great for debugging, but it can't see your actual user mix. Optimize against field data; debug with lab data.

See it

Live demo
Tweak it3
Switch between LCP, INP, and CLS to see what each measures and the good / poor thresholds.

Switch between the three metrics and read what each one measures, the good / needs-improvement / poor thresholds with their numeric boundaries, and the common causes paired with their fixes. The boundaries are spelled out as text, so the rating never depends on color alone.

How it works

Each vital has a "good" target and a small set of things that reliably move it.

LCP — good ≤ 2.5s (needs-improvement ≤ 4.0s, poor above). The usual culprits are a slow server (high TTFB), render-blocking CSS and JS, an unoptimized hero image, or lazy-loading something that's actually above the fold. Fixes: cache and use a CDN, inline critical CSS and defer non-critical scripts, serve right-sized modern image formats and preload the LCP image, and eager-load the hero.

INP — good ≤ 200ms (needs-improvement ≤ 500ms, poor above). It's driven by long tasks that block the main thread, heavy event handlers, and shipping too much JavaScript. Fixes: break long tasks into chunks and yield (scheduler.yield), do less synchronous work in handlers, and code-split so input isn't queued behind a giant bundle.

CLS — good ≤ 0.1 (needs-improvement ≤ 0.25, poor above). Shifts come from images without dimensions, content or ads injected above existing content, and web fonts swapping in at a different size. Fixes: reserve space with aspect-ratio or explicit width/height, hold fixed slots for dynamic content, and tame font swaps.

Measurement comes from two places. Field data is collected from real Chrome users via the Chrome User Experience Report (CrUX) and your own RUM, and Google scores it at the 75th percentile (p75) — so a quarter of your users can still be slower than your "good" number. Lab data comes from a synthetic run in Lighthouse or PageSpeed Insights: one controlled environment, perfect for finding regressions but blind to your real audience.

Build it

The single highest-leverage CLS fix is reserving space for media and dynamic content before it loads, so nothing pushes the layout when it arrives.

CSS
Reserve space so content never shifts (CLS fixes)
/* Give media an intrinsic box: the browser reserves height before load. */
.media {
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;
}
 
/* Or set explicit dimensions on <img>/<video> — width/height attributes
   plus this rule let the browser compute the box from the start. */
img,
video {
  height: auto;
}
 
/* Reserve a fixed slot for content injected later (ads, embeds, banners)
   instead of letting it shove everything below it down the page. */
.ad-slot {
  min-height: 250px;
}
 
/* font-display: swap shows a fallback immediately; pair it with size-adjust
   on @font-face so the swap doesn't reflow the text and trigger CLS. */
.body-text {
  font-display: swap;
}

Make it yours

Use the controls beside the demo above to change core web vital, sample reading lands in, and show field-data note — each change updates the example live.

Experiment in the playground
  • Flip through LCP, INP, and CLS and commit the three good thresholds to memory: 2.5s, 200ms, 0.1.
  • For your own site, pull the field numbers from CrUX or PageSpeed Insights and find which vital is in the red — that's where the work is.
  • Pick one cause from the demo's list and trace it in your codebase: an un-sized image, a long task, a render-blocking script.

Reproduce it with an LLM

Reproduce it with an LLM

You are a web-performance consultant. For a content-heavy marketing page that fails its Core Web Vitals, write a prioritized plan to get LCP, INP, and CLS into the 'good' range. State the good thresholds (LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1) and for each metric give the top causes and concrete fixes: for LCP — prioritize the hero image (preload, responsive sizes, no lazy-load above the fold), cut render-blocking resources, fast server/CDN; for INP — break up long tasks, defer non-critical JS, keep event handlers light; for CLS — set width/height or aspect-ratio on media, reserve space for ads/embeds, avoid inserting content above existing content, use font-display swap with size-adjust. Note how to MEASURE each (field via CrUX/RUM vs lab via Lighthouse). Return a prioritized checklist grouped by metric.

Pitfalls & accessibility

  • Watch CLS during interactions too: a layout shift caused by a user's own tap is excluded, but one that lands a fraction of a second after — moving the button they were aiming for — still counts and still frustrates.

Further reading