Skip to content

Test your website now

Free, no card. We read only public pages.

Run free audit →

Performance

Core Web Vitals: what actually moves LCP, INP and CLS

The three metrics Google ranks on, what makes each one bad in practice, and why a lab tool can tell you about two of them but never about the third.

Published

The three metrics, and what each one is really asking

Core Web Vitals are three numbers Google collects from real Chrome users and uses as a ranking signal. Each is a proxy for one question a visitor asks without thinking about it.

  • LCP - Largest Contentful Paint. 'Has the main thing appeared yet?' Good is 2.5 s or under; over 4 s is poor.
  • INP - Interaction to Next Paint. 'Did anything happen when I tapped?' Good is 200 ms or under; over 500 ms is poor.
  • CLS - Cumulative Layout Shift. 'Did the thing I was about to tap move?' Good is 0.1 or under; over 0.25 is poor.

The thresholds are pass/fail at the 75th percentile of your real traffic, not at the average. That detail decides most arguments about them: a site where three visitors in four have a good experience passes, and a median that looks fine can still fail because the slowest quarter is very slow.

Lab and field: why two tools disagree about your site

There are two ways to get these numbers and they answer different questions. Field data is collected from real Chrome users on real devices and networks - it is what Google actually ranks on, it needs enough traffic to exist at all, and it is a 28-day trailing window, so a fix you shipped today shows up in about a month.

Lab data comes from one synthetic load in a controlled browser. It is available instantly, on any URL, including a staging site with no users. It is reproducible, which is the whole point of it - and it is not what you are ranked on.

Use lab data to find causes and field data to know where you stand. A team that optimises only for the lab number ends up tuning a simulation; a team that watches only field data waits a month to learn whether each change helped.

This is also why one tool says your site is fine and another says it is failing. They are usually both right about different questions.

INP: the one no lab tool can measure

INP measures how long the page takes to paint a response after a real interaction - a tap, a click, a key press. A synthetic load nobody interacts with therefore has no INP at all. Not a bad INP: none. Any tool reporting an INP figure from a single automated page load is reporting something else and calling it INP.

What lab tools measure instead is Total Blocking Time: the portion of every long task over 50 ms while the main thread was busy between first paint and load. Good is 200 ms or under; over 600 ms is poor. TBT is a decent predictor of INP because the same blocked main thread causes both - if a task is hogging the thread, an interaction arriving during it waits.

We measure TBT, and this page says so rather than claiming an INP figure we cannot obtain. For real INP you need field data: the Chrome UX Report if your site has the traffic for it, or a small script in your own pages using the web-vitals library, which works at any traffic level.

What actually moves TBT, and with it INP, in rough order of how often it is the answer:

  • Third-party tags. Tag managers, session recorders, chat widgets and A/B testing tools are usually the largest single block of main-thread time on a marketing site, and the one nobody owns.
  • A large JavaScript bundle executed on load rather than split at route boundaries.
  • Hydration of a page that did not need to be interactive - long lists, footers, and content rendered as components purely for authoring convenience.
  • Work that should not be on the main thread at all: parsing large JSON, image manipulation, heavy client-side filtering.

LCP: find the element first, then fix the right thing

Almost every wasted hour spent on LCP starts with optimising the wrong element. Open DevTools, take a performance trace, and read which element the browser actually recorded as the largest contentful paint. It is frequently not the hero image - a heading in a webfont, a background, or a video poster is just as likely.

Once you know the element, LCP decomposes into four parts, and knowing which one dominates tells you what to do:

  • Time to first byte - the server thinking. If TTFB alone is over ~800 ms, nothing you do to the image matters; fix caching or the origin first.
  • Resource load delay - the gap between the HTML arriving and the browser starting to fetch the LCP resource. This is the one preloading fixes, and it is usually caused by the resource being discovered late: referenced in CSS, injected by script, or behind a lazy-load attribute it should never have had.
  • Resource load time - the download itself. Format and dimensions, not compression settings: an image served at 2400px into a 600px slot is the usual culprit.
  • Render delay - the resource has arrived and the page still has not painted, because a render-blocking stylesheet or a synchronous script is in the way.

The single most common own-goal: loading="lazy" on the LCP image. It is correct on everything below the fold and actively harmful on the one element the metric is measuring, because it delays the fetch until layout has decided the element is visible.

Measure mobile separately. Most traffic is mobile, Google's signals use the mobile measurement, and a site that is comfortably under 2.5 s on a desktop connection can be over 4 s on a phone - different image, different network, different CPU.

CLS: reserve the space before you need it

CLS is the sum of how much visible content jumped without the user causing it. Unlike the other two it is rarely a performance problem - it is a markup problem, and it is usually fixable in an afternoon by someone who knows where the offending components are.

  • Images and video without width and height, or without an aspect-ratio. The browser cannot reserve the space, so everything below moves when the file arrives.
  • Ads, embeds and iframes dropped into a container that sizes itself to its contents.
  • Banners, cookie notices and promo bars inserted at the top of the document after first paint - they push the entire page down, which is the largest shift a site can produce.
  • Webfonts swapping in at a different metric than the fallback, moving every line of text a little.
  • Content injected above the current scroll position by an A/B test or a personalisation script.

CLS accumulates over the life of the page, not just during load, so a shift caused by an infinite scroll or a late-loading recommendation strip counts too. If your number is bad and load looks clean, keep the trace running and interact with the page.

The boring fixes that move all three

Before any metric-specific work, four things are worth checking because they are cheap, they are frequently wrong, and each one improves more than one number at a time.

  • Compression on text responses. HTML, CSS, JS and JSON should be served with gzip at minimum and brotli where available. A surprising number of sites compress the HTML and miss the API responses on the same domain.
  • Cache-Control on static assets. Fingerprinted files should be immutable with a long max-age; without it every repeat visit re-downloads what it already had.
  • Image format and size. Modern formats at the dimensions actually rendered, with srcset so a phone does not download the desktop asset.
  • Server response time. If the origin takes 900 ms to think, every metric downstream inherits it, and no front-end work will recover it.

Then re-measure. The one discipline that separates performance work from performance opinions is recording the number before the change and after it - which is also the difference between an audit tool that helps and one that hands you a list.

In the product

The checks this maps to

Every check named here runs on every scan, on every plan - what a plan changes is how many pages are crawled, how long the history is kept and what you can do with the result. These are the ids the findings carry, so you can match a line in a report to the explanation above.

  • perf.cwv.lcp - Largest Contentful Paint under 2.5 s
  • perf.cwv.cls - Cumulative Layout Shift under 0.1
  • perf.cwv.tbt - Total Blocking Time under 200 ms
  • perf.cwv.fcp - First Contentful Paint under 1.8 s
  • perf.ttfb - Server responds quickly (TTFB)
  • perf.compression - Text responses compressed
  • perf.cache-headers - Cache-Control configured
  • perf.mobile.lcp - Mobile LCP under 2.5 s
  • mobile.responsive-images - Responsive images in use