⌨️ Web development

Core Web Vitals explained: LCP, INP and CLS without jargon

What LCP, INP and CLS measure, the recommended thresholds, how to read lab and field data and which changes genuinely improve each of the three metrics.

Core Web Vitals are three metrics Google uses to describe the loading experience, responsiveness and visual stability of a page. They have become a common reference because they turn vague impressions ("the site is slow", "the page jumps") into measurable numbers. Knowing them well lets you act where it matters, instead of chasing the score of a single tool.

The three metrics

LCP – Largest Contentful Paint

It measures the time it takes for the largest content element visible in the viewport to be rendered: typically the main image, a cover video or a large block of text. It indicates how quickly users perceive that the page "has arrived". A good value is within 2.5 seconds.

INP – Interaction to Next Paint

It measures responsiveness: for user interactions (clicks, taps, key presses) it calculates the time between the action and the moment the browser manages to show a visual update. The reported value essentially represents the slowest interactions observed during the visit. INP replaced the earlier FID metric, which only measured the delay of the first interaction. A value within 200 milliseconds is considered good.

CLS – Cumulative Layout Shift

It measures visual stability: how much elements move unexpectedly during the life of the page. The classic example is text sliding down just as you are about to tap a link, because a banner has loaded above it. It is a unitless score; a value within 0.1 is considered good.

Metric What it measures "Good" threshold
LCP loading of the main content ≤ 2.5 s
INP responsiveness to interactions ≤ 200 ms
CLS unexpected layout shifts ≤ 0.1

To assess a site, you look at the 75th percentile of real visits: at least three visits out of four must fall within the threshold.

Lab data and field data

This is a fundamental and often misunderstood distinction.

  • Lab data: simulated measurements on a predefined device and network, such as those from Lighthouse in the developer tools. They are repeatable and excellent for debugging, but they do not represent your users.
  • Field data: measurements collected from real users' browsers. The Chrome User Experience Report and the matching section of PageSpeed Insights show this data, when the site has enough traffic.

INP in particular depends on real interactions and is hard to measure in the lab, where nobody clicks. When the two kinds of data disagree, field data describes the true experience.

You can also collect field data yourself with the open-source web-vitals library:

import { onLCP, onINP, onCLS } from 'web-vitals';

function send(metric) {
  navigator.sendBeacon('/analytics', JSON.stringify({
    name: metric.name,
    value: metric.value
  }));
}

onLCP(send);
onINP(send);
onCLS(send);

How to improve LCP

The LCP element is often an image, so the most effective changes concern the image itself:

  1. Make it discoverable early: the main image must be in the initial HTML, not injected by JavaScript or loaded as a CSS background that the browser discovers late.
  2. Do not lazy-load it: loading="lazy" on an above-the-fold image delays LCP. Use fetchpriority="high" instead.
  3. Reduce its weight: modern formats such as WebP or AVIF and dimensions suited to the screen.
  4. Shorten server response time: a page served from a cache or a CDN starts sooner.
  5. Remove render-blocking resources: huge CSS files and synchronous scripts in the head delay everything.
<img src="/hero.avif" width="1200" height="600"
     fetchpriority="high" alt="Description of the image">

How to improve INP

A high INP almost always means the browser's main thread is busy with JavaScript when the user interacts.

  • Break up long tasks: a loop lasting hundreds of milliseconds blocks every response. Split it into chunks and yield to the browser between them.
  • Do the visual update first: in a click handler, show feedback immediately (for example a "loading" state) and defer the heavy work.
  • Reduce third-party JavaScript: widgets, trackers and embedded chats compete for the same thread.
  • Avoid huge DOM updates: redrawing thousands of nodes on every key press is expensive.
button.addEventListener('click', async () => {
  button.textContent = 'Processing…';
  await new Promise(r => setTimeout(r, 0)); // let the browser paint
  processData();
});

How to improve CLS

Layout shifts almost always come from unreserved space:

  • set width and height (or aspect-ratio) on images, videos and iframes, so the browser reserves the space before the download;
  • reserve an area with a minimum height for banners, ads and embedded content;
  • do not insert content above what is already visible, except in response to a user action;
  • handle web fonts so that swapping the typeface does not change the text size too much;
  • for animations use transform rather than properties such as top or height, which push other elements around.

A healthy perspective

Metrics are a means, not an end. A perfect lab score with mediocre field data is worthless, and a slight drop in one metric may be acceptable if it brings a genuinely useful feature. Focus on the pages with the most traffic and on the problems that real data shows most often.

In short

  • LCP measures loading of the main content, INP responsiveness, CLS visual stability.
  • Good thresholds are 2.5 seconds, 200 milliseconds and 0.1, assessed at the 75th percentile.
  • Field data describes real users; lab data is for debugging.
  • For LCP the main image matters most; for INP, JavaScript on the main thread.
  • CLS is fixed by reserving space for elements that arrive later.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also