Build

Core Web Vitals in plain language

A page can be complete in the HTML and still feel slow, unresponsive or jumpy. Core Web Vitals are Google's published way of putting a number on each of those feelings, with fixed pass marks. They are easy to quote and easy to misread, so this page covers what each one counts, what moves it, and how to find the cause when one is red.

The three metrics and their thresholds

web.dev states each target and the rule for judging them: the 75th percentile of page loads, segmented across mobile and desktop. A page passes when it meets all three targets at that percentile. One fast test on your own laptop says very little about that.

MetricWhat it measuresGoodNeeds improvementPoor
Largest Contentful Paint (LCP)Time until the largest image or text block in the viewport is rendered2.5 s or less2.5 s to 4.0 sover 4.0 s
Interaction to Next Paint (INP)How long the page takes to show a visual response after a click, tap or key press200 ms or less200 ms to 500 msover 500 ms
Cumulative Layout Shift (CLS)How much visible content moves unexpectedly0.1 or less0.1 to 0.25over 0.25

The "good" and "poor" cut-offs are published in web.dev's thresholds methodology; the middle band is whatever lies between them. INP replaced First Input Delay as a Core Web Vital on 12 March 2024, according to the web.dev announcement. FID measured only the input delay of the first interaction. INP covers every interaction and runs through to the next frame being painted.

A fictional 75th percentile

Twenty fictional visits to one page recorded these LCP values in seconds: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.6, 2.8, 3.1, 3.6, 4.4 and 5.2. The median is 2.05 s and 70 percent of visits were under 2.5 s, which feels healthy. Taking the 75th percentile by the simple nearest-rank method (the fifteenth value of twenty) gives 2.6 s, so the page fails. CrUX aggregates differently, so this illustrates the rule, not its arithmetic: the slowest quarter of visits decides the verdict.

Lab data and field data

The same page gives different numbers depending on where they come from.

  • Lab data comes from a controlled run: Lighthouse, or the DevTools Performance panel. It is repeatable and available before launch, and it is the right tool for finding a cause.
  • Field data comes from real browsers visiting the real page, collected in the Chrome User Experience Report (CrUX) and shown in PageSpeed Insights and Search Console. It reflects your actual audience, with their phones and networks.

web.dev's guide to why lab and field data differ notes that CrUX reports a distribution over a 28-day period, so a fix takes weeks to show fully in field data. A site is only included in CrUX if it meets eligibility criteria: it has to be publicly discoverable and have enough visitors. When a page lacks enough traffic for its own figures, PageSpeed Insights falls back to origin-level data for the whole site, which can differ a lot from one page's experience.

Lighthouse loads the page in a simulated environment with no user, so it cannot report INP. web.dev names Total Blocking Time as the lab proxy: improvements to it may improve INP in the field, but it is not a substitute. Lab CLS is also a floor. A lab tool only sees shifts during load, so the field figure can be higher. Treat a green lab score as "no obvious problem found" and field data as the verdict.

LCP: what appears, and when

LCP measures from the moment the visitor starts navigating to the render of the largest image or text block in view. That includes redirect time and server response time, not only the page's own work. Time to LCP breaks into four consecutive parts, and knowing the split tells you where to work.

  1. Time to first byte. From starting the navigation to the first byte of the HTML.
  2. Resource load delay. From there until the browser starts fetching the LCP resource. If the LCP element is text in a system font, this is zero.
  3. Resource load duration. How long the file itself takes to download.
  4. Element render delay. From the file arriving to the element being fully rendered.

web.dev gives a rule of thumb for a well-optimised page: roughly 40 percent of LCP in time to first byte, under 10 percent in load delay, roughly 40 percent in load duration and under 10 percent in render delay. It warns against turning those percentages into fixed millisecond targets, because the parts only make sense relative to each other. It also warns that one fix can merely move time between parts. If the image is hidden by a script until the script loads, a smaller file shortens the download but adds the same time to the render delay, and LCP does not change.

Common fixes, in rough order of payoff:

  • Make the LCP image discoverable in the initial HTML as an <img> with its src or srcset present. A background image set in CSS or an image added by JavaScript cannot be found by the browser's preload scanner, so it starts late. If it must stay in CSS, preload it with a high fetch priority.
  • Never put loading="lazy" on the LCP image. web.dev suggests fetchpriority="high" on the image you expect to be the LCP element, and warns that setting it on more than one or two images makes the hint unhelpful.
  • Serve the right size and a modern format (see images in 2026).
  • Send complete HTML from the server. A page that needs a JavaScript bundle before any text exists loses the first two phases to waiting, a point developed in static HTML first.
  • Keep render-blocking CSS small so the element is not held back after its file arrives.

INP: how quickly the page answers

INP observes every click, tap and key press during a visit and reports a single value that all, or nearly all, interactions were below. For most pages that is the slowest interaction. On pages with many interactions, web.dev says one highest interaction is ignored for every 50, so one freak event does not dominate. The figure is finalised when the visitor leaves, and then the 75th percentile across page views is reported. Hovering, scrolling and zooming are not observed.

A page can report no INP at all if nobody clicked, tapped or typed.

Each interaction has three parts: input delay (the main thread was busy when you tapped), processing time (your event handlers running), and presentation delay (the browser working out and painting the next frame). The cure for all three is usually the same: keep the main thread free.

  • Split long tasks. web.dev defines a long task as anything over 50 milliseconds. Break work into chunks and yield between them.
  • Do the minimum inside the handler. Update the visible state first, then do analytics, storage and non-critical work afterwards.
  • Reduce the amount of DOM the browser must re-style after a change. web.dev notes that rendering work grows with DOM size, so a large document makes every update slower.
  • Defer scripts that do not affect the first screen. Script evaluation (parsing, compiling, running) during load can extend input delay if someone taps while it is happening.

A page that is mostly static HTML with a small amount of script is less exposed here, because there is little to block.

CLS: whether the page stays still

A layout shift happens when a visible element changes its start position between two rendered frames. Each shift scores the impact fraction (how much of the viewport the moving elements cover, before and after) multiplied by the distance fraction (how far they moved relative to the viewport's larger dimension). CLS reports the largest burst, which web.dev calls a session window: shifts less than a second apart, with a maximum of five seconds in total.

A worked case with fictional numbers: a banner inserted above the article pushes an element that fills half the viewport down by 10 percent of the viewport height. The element's footprint in both positions covers 60 percent of the viewport, so the impact fraction is 0.6 and the distance fraction is 0.1, giving a score of 0.06. That alone is good. A late-loading font that nudges the same block again in the same window adds another 0.06, and the window now totals 0.12, over the 0.1 line. Two small shifts in one burst can fail a page that either one alone would not.

Shifts within 500 milliseconds of a tap, click or key press are flagged as caused by input and can be excluded. That does not cover scrolling, dragging or pinch-zooming, which are not treated as recent input. Animating with transform rather than top, left or height avoids shifts entirely, which web.dev recommends.

The usual causes and their fixes:

  • Images and video without dimensions. Add width and height attributes, or aspect-ratio in CSS. Browsers compute an aspect ratio from the attributes before the file arrives, so the box exists at the right height.
  • Late fonts. A fallback font and the final font have different metrics, so lines reflow on swap. See font loading without layout shift.
  • Injected content. Banners, consent bars and embeds that push content down. Reserve their space, or overlay them rather than inserting them above existing content.
  • Ads and iframes with unknown height. Give the slot a minimum height.

Diagnosing a red metric

SymptomLikely causeFirst thing to check
LCP high on every pageSlow server response, redirects or render-blocking CSSTTFB in the Network panel, then the CSS loaded in the head
LCP high only on pages with a large imageImage too big, lazy-loaded, or found lateThe LCP element in the Performance panel, then its loading and fetchpriority attributes
INP high after opening a menu or filterA handler doing heavy work synchronouslyLong tasks in the Performance panel, aligned with the interaction
INP high only on first tapScripts still being evaluated at loadTotal Blocking Time and the size of the main bundle
CLS high only on first visitUncached fonts or images swapping inLayout Shift regions in the Performance panel, with the cache disabled
CLS high only on pages with embedsEmbed container has no reserved heightThe shifted element highlighted by DevTools

One slow page, taken apart

Take a fictional page, example.com/studio/, with a field LCP of 3.4 seconds. A lab run splits it into 0.4 s to first byte, 1.3 s of resource load delay, 1.0 s of load duration and 0.7 s of render delay. As shares of the total those are 12, 38, 29 and 21 percent. Against web.dev's guideline, load delay should be under 10 percent and render delay under 10 percent, so both are over, and load delay is the larger. The cause turns out to be that the hero image is set as a CSS background by a script, so the browser cannot see it until the script runs.

Moving the image into the HTML as an <img> with fetchpriority="high" should shrink that 1.3 seconds, because the request can then start with the stylesheet. It will not touch the 0.7 s of render delay, which points at something else holding the paint back, such as a stylesheet or a script that hides the page. Fix the largest part, re-measure, and read the split again, because the largest part is rarely the one people guess.

Google's page experience documentation says Core Web Vitals are used by its ranking systems, that there is no single page experience signal, and that good results in a Core Web Vitals report do not guarantee pages rank at the top of Search. It adds that chasing a perfect score only for SEO may not be the best use of your time, and that Search will show the most relevant content even when page experience is weak. Use the numbers to remove friction for visitors, not as a lever.

Checklist

  • Measure with field data when it exists, and lab data to find the cause.
  • Read the LCP split into its four parts before changing anything.
  • Keep the LCP image in the HTML, non-lazy, with high fetch priority.
  • Keep long tasks under 50 ms and handlers light to protect INP.
  • Reserve space for images, embeds and fonts, and count shifts in bursts, not one at a time.

Questions and answers

What are the Core Web Vitals thresholds?

A page is rated good when Largest Contentful Paint is 2.5 seconds or less, Interaction to Next Paint is 200 milliseconds or less, and Cumulative Layout Shift is 0.1 or less, each measured at the 75th percentile of page loads and segmented across mobile and desktop.

Why does Lighthouse not show my INP?

A standard Lighthouse run loads the page without a user, so there is no click, tap or key press to time. web.dev points to Total Blocking Time as the lab proxy. Real INP comes from field data.

Do good Core Web Vitals guarantee better rankings?

No. Google's page experience documentation says its ranking systems use Core Web Vitals, and also that good results do not guarantee your pages rank at the top of Search.

Can I measure Core Web Vitals on a brand-new site?

Lab tools work immediately. Field data comes from the Chrome User Experience Report, which only includes sites that meet its eligibility rules, so a new site often shows lab numbers only for a while.

Sources

  1. Web Vitals overview (web.dev) web.dev
  2. Optimize Largest Contentful Paint (web.dev) web.dev
  3. Interaction to Next Paint (web.dev) web.dev
  4. Cumulative Layout Shift (web.dev) web.dev
  5. Understanding page experience in Google Search results developers.google.com