Code of the Day
AdvancedWeb Performance & Delivery

Core Web Vitals

Core Web Vitals are Google's three user-experience metrics — LCP, INP, and CLS — that measure loading speed, interactivity, and visual stability.

Web FoundationsAdvanced8 min read
By the end of this lesson you will be able to:
  • Name the three Core Web Vitals and their target thresholds
  • Identify what causes a poor score for each metric
  • Choose the right measurement tool for lab data versus real user data
  • Explain the mental model — loading feel, responsiveness feel, and stability feel

Google made a change in 2021 that sent a shockwave through the industry: search ranking now includes page experience signals. At the centre of those signals are Core Web Vitals — three metrics that capture how fast, responsive, and stable a page feels to a real user. But even setting aside search rankings, they are worth caring about because they correlate closely with bounce rate, session length, and conversion. A page that feels slow loses users before they read a word.

You already understand the and what means at a pipeline level. This lesson maps that knowledge onto the three vitals, shows you what moves the needle on each, and explains how to measure them accurately.

LCP — does the page feel loaded?

Largest Contentful Paint measures the moment the browser renders the single largest visible element. This is almost always the hero image, the article headline, or the above-the-fold featured photo — the element that dominates the viewport on arrival.

Target: under 2.5 seconds from navigation start.

Four root causes account for most poor LCP scores:

CauseExample
Slow server responseTime to first byte (TTFB) above 600 ms
Render-blocking resourcesA large synchronous <script> or unused CSS in <head>
Slow image downloadAn uncompressed 3 MB JPEG as the hero
Late image discoveryThe hero <img> is below the fold in the HTML or injected by JS

The fix follows from the cause: a fast TTFB needs server-side work (CDN, caching); a slow image needs format optimisation and fetchpriority="high"; late discovery needs a <link rel="preload"> in <head> so the browser starts fetching immediately.

LCP only counts elements visible in the viewport at the time they render. An enormous image at the bottom of the page does not count. This keeps the metric focused on what the user actually sees first.

INP — does the page feel responsive?

Interaction to Next Paint replaced the older First Input Delay metric in March 2024. Where FID only measured the first interaction ever on the page, INP measures every interaction — clicks, key presses, taps — and reports the worst-case delay across the entire session.

Target: under 200 ms from interaction to the next painted frame.

is high when JavaScript tasks on the main thread are too long. When the main thread is busy — running a large event handler, parsing a large script, or executing a heavy framework re-render — user interactions queue up and wait. The browser cannot paint a response until the blocking work finishes.

What causes long tasks:

  • Event handlers that do expensive DOM updates synchronously
  • Synchronous third-party scripts (analytics, chat widgets) that run on every interaction
  • Framework components that re-render large subtrees when a tiny piece of state changes

Fixes: break long tasks with scheduler.yield() or setTimeout(fn, 0), move work off the main thread with Web Workers, and audit third-party scripts that fire on every click.

CLS — does the page feel stable?

Cumulative Layout Shift measures unexpected movement of visible elements after they first render. A banner that loads two seconds in and pushes the article text down by 200 pixels creates a jarring experience — and a high CLS.

Target: under 0.1. The score is a product of the fraction of the viewport affected and the distance elements move, so a large element shifting a long way is penalised heavily.

has a small set of well-understood causes:

  • Images and video without width/height: the browser does not reserve space before the resource loads, so the surrounding content jumps when it arrives.
  • Web fonts that swap: a fallback font occupies one amount of space; when the custom font loads, metrics differ and text reflows.
  • Late-loading banners and ads: content injected by third-party scripts after the main content has already been laid out.
  • Dynamically inserted UI: a cookie consent banner or chat bubble that appears above existing content.

Fixes: always declare width and height on <img> and <video>; use font-display: optional or font-display: swap with a size-adjusted fallback; reserve space for ad slots with explicit CSS dimensions.

How to measure

There are two categories of data, and you need both:

Lab data is produced by running a synthetic test against your page in a controlled environment. It is reproducible and available to anyone with a URL:

  • Chrome DevTools Lighthouse — run from the DevTools panel; gives scores and per-metric diagnostics with suggestions.
  • PageSpeed Insights — Google's hosted Lighthouse; also shows field data when available.
  • WebPageTest — richer waterfall views; useful for diagnosing network-level causes.

Field data (also called Real User Monitoring, or RUM) is collected from actual users' browsers. Lab data cannot capture the variety of real-world devices, networks, and user behaviour patterns:

  • Chrome User Experience Report (CrUX) — Google's aggregate of real-user metrics for millions of URLs; shown in PageSpeed Insights.
  • web-vitals JS library — drop in a few lines of JavaScript to measure LCP, INP, and CLS in your own users' sessions and send the data to your analytics backend.
import { onLCP, onINP, onCLS } from 'web-vitals';

onLCP(console.log);
onINP(console.log);
onCLS(console.log);

Lighthouse scores in your local DevTools can differ significantly from real user data. Throttled lab conditions simulate a slow device, but your actual users span cheap Android phones on 3G to MacBooks on fibre. Always check PageSpeed Insights' field data before concluding that your site is fast.

The mental model

The three vitals each answer one user question:

VitalQuestion answered
LCP"Is the page loaded?"
INP"Is the page responding to me?"
CLS"Is the page stable enough to read?"

A site can pass two and fail one badly. A performant server with great image optimisation (good LCP) can still feel broken if a third-party chat script blocks every click (poor INP), or if an ad network shifts the article text every few seconds (poor CLS). Each vital is independently actionable.

Where to go next

LCP is dominated by one thing above all others: the hero image. The next lesson goes deep on Image Optimization — format choices, compression, responsive delivery, lazy loading, and the fetchpriority attribute — giving you the full toolkit for the most common LCP bottleneck.

Finished reading? Mark it complete to track your progress.

On this page