Skip to content

Core Web Vitals: what they are and how to measure them without fooling yourself

By Carlos Alberto Cardona B. · · 8 min read

You ran PageSpeed Insights, got a number, ran it again and got a different one. Neither number is a lie and neither is the answer. The three metrics underneath it are, and they are worth understanding before you change a line of code.

Core Web Vitals are three field metrics that Google uses to describe real user experience. web.dev keeps the current definition and thresholds, and they change, so it is worth reading the source rather than a blog post from two years ago.

Everything below is measured on this site, including a measurement of mine that turned out to be worthless and why.

What LCP, INP and CLS measure

MetricGoodNeeds workPoorWhat it measures
LCP≤ 2.5 s≤ 4.0 s> 4.0 sWhen the largest visible element finishes painting
INP≤ 200 ms≤ 500 ms> 500 msHow long the page takes to respond to an interaction
CLS≤ 0.1≤ 0.25> 0.25How much the layout moves while it loads

«Largest» in LCP means largest by visible area inside the viewport, which is rarely the element you would have guessed. INP replaced FID because responding to the first click is easier than responding to all of them.

And CLS does not care whether the movement annoyed anyone: it scores the shift whether or not a person was looking. That detail matters later.

Lab against field, and why the score is not the metric

The 0 to 100 number PageSpeed shows comes from Lighthouse, in a lab, on a simulated device. The three metrics that Google actually uses come from the Chrome UX Report, which is real Chrome users over the last 28 days.

If your site is new or has little traffic, the field section says «no data» and it will keep saying it. That is not a failure: a lab number is not a field number, and until the field data exists you say it does not exist.

Why a single run lies

Lighthouse's own documentation is explicit that most of the variance in a score comes from the environment, not from the page: network, CPU contention, A/B tests, third-party scripts that answer at different speeds.

So one run is a sample of one. I take five, keep the median and report the range next to it. A page whose median is 2.1 s with a range of 1.8 to 5.9 s has a different problem from one that sits at 2.1 s every time.

From the browser · 8 min

Why your performance score changes by itself

That one speed measurement is not a data point. Lighthouse's own documentation says most of the variance comes from the environment, not from the page.

  1. 1.

    Open PageSpeed Insights and paste your page URL.

  2. 2.

    Run it five times in a row, changing nothing on the site.

  3. 3.

    Write down the LCP of each run. Five numbers.

What comes out tells you which of the three cases you are in.

The five are close: say 2.1 · 2.2 · 2.0 · 2.3 · 2.1.

The median is a data point and you can decide on it. Your page is stable.

They scatter: 2.1 · 5.9 · 2.4 · 1.8 · 4.7.

Your problem is not the median, it is the spread. Something external —a third-party script, an ad, a font— answers at a different speed each time.

The «real user data» section says there is no data.

Your site does not have enough traffic for the field report. What you are seeing is the lab, and it is not the same thing. You say so; you do not paper over it.

What this check does NOT prove: Five runs give you an idea, not a distribution. And the lab simulates one device on one network: what your visitor lives is in the field data, when it exists.

Which element is your LCP, really

On this site I assumed the hero image was the LCP element. It was the H1 heading, and the image occupied roughly seven times more area. Chrome did not count it because it had not finished decoding inside the measured window.

That mistake cost me an afternoon optimising the wrong thing. The fix is not to guess: Chrome DevTools marks the LCP element on the Performance panel, and the PerformanceObserver API reports it in four lines.

$ the LCP element, from the browser console
new PerformanceObserver((list) => {
  const e = list.getEntries().at(-1);
  console.log(e.element, Math.round(e.startTime));
}).observe({ type: 'largest-contentful-paint', buffered: true });
→ paste it, reload, and read which element it actually was

Two measurements from this site, one of them useless

First measurement: LCP with and without an above-the-fold image, on localhost. 32 ms against 28 ms. I nearly reported that the image did not matter.

It was an instrument failure, not a finding. On localhost there is no latency, so the two arms could not differ. With mobile throttling applied (1.6 Mbps, 150 ms round trip, CPU slowed four times) the same comparison gave 688 ms against 276 ms, and the conclusion reversed.

The second one: this site scored CLS 0.3246 on 45 of its 48 routes, reproducible four times out of four. The cause was a loading placeholder shorter than the screen, so the footer was visible and then moved down. One CSS line took it to 0.000.

What to fix first

  • LCP: find the real element first. If it is an image, give it explicit width and height, serve it in a modern format and let it load eagerly. If it is text, the problem is usually a web font blocking the paint.

  • CLS: reserve the space of anything that arrives late. Images without dimensions, ads, embeds, and loading placeholders shorter than the viewport, which is the one that caught me.

  • INP: it is almost always JavaScript holding the main thread. Break long tasks up and postpone whatever is not needed for the first interaction.

  • And measure again, with the same instrument and the same conditions. A change you cannot compare is a change you cannot defend.

You can run all of this yourself at PageSpeed Insights, five times, and write down the median and the range. That is the whole method.

Watch it explained, by the people who built it

Three short videos from Google's own channels. They are the same source the links above point to, in video form:

Sources

Consulted on September 27, 2026. Each date is the source's last published update.

  1. What is Interaction to Next Paint (INP)? · Chrome for Developers · vídeo
  2. How to improve Largest Contentful Paint for a better page experience · Google Search Central · vídeo
  3. Monitor live Core Web Vitals in Chrome DevTools · Chrome for Developers · vídeo
  4. Web Vitals · web.dev
  5. Largest Contentful Paint (LCP) · web.dev
  6. Interaction to Next Paint (INP) · web.dev
  7. Cumulative Layout Shift (CLS) · web.dev
  8. Chrome UX Report · Chrome for Developers
  9. Lighthouse performance scoring and variability · Chrome for Developers
  10. PageSpeed Insights · Google

Site loading slowly and you do not know where to start?

The diagnosis is free: I measure several times and tell you what is actually holding the page back.

Get a free diagnosis