Skip to content
fearchitect
Performance Engineering

Core Web Vitals

Google's three user-experience metrics: LCP, INP, and CLS.

By Abas TurabliReviewed

Summary

Core Web Vitals are three field-measurable metrics that quantify real user experience: LCP tracks loading, INP tracks responsiveness (replacing FID in March 2024), and CLS tracks layout stability. Good thresholds are LCP ≤2.5 s, INP ≤200 ms, CLS ≤0.1. They drive Google Search ranking and are the sharpest signal for user-facing performance regressions.

Jump to the interview angle

The three Core Web Vitals at a glance

MetricWhat it measuresGood thresholdTop cause of failure
LCPTime to render largest above-fold image or text block≤ 2.5 sSlow TTFB, render-blocking CSS, unpreloaded hero image
INP98th-percentile input-to-next-paint delay across all interactions≤ 200 msLong JS tasks blocking main thread between input and frame
CLSSum of impact × distance for unexpected layout shifts≤ 0.1Images or iframes without explicit dimensions, late-injected DOM

Lab vs field data

Lighthouse (lab) runs a single cold load in a throttled environment — good for catching regressions in CI. CrUX (field) reports 28-day rolling percentiles from real Chrome users and is what Google uses for Search ranking. A score that looks fine in Lighthouse can still fail in the field if ads, late fonts, or real interactions spike the metrics.

Measure all three vitals with web-vitals v4

Install web-vitals (npm package by Google). Each on* function fires when the metric value is ready and again on updates. Pass reportAllChanges: true so INP updates as the user interacts rather than only on page unload.

RUM instrumentation with web-vitals v4ts
import { onLCP, onINP, onCLS } from "web-vitals";
import type { Metric } from "web-vitals";

function send(metric: Metric): void {
  // Replace with your RUM endpoint or analytics call.
  navigator.sendBeacon("/vitals", JSON.stringify({
    name: metric.name,    // "LCP" | "INP" | "CLS"
    value: metric.value,  // ms for LCP/INP, unitless for CLS
    rating: metric.rating, // "good" | "needs-improvement" | "poor"
    id: metric.id,
    delta: metric.delta,
  }));
}

onLCP(send);
onINP(send, { reportAllChanges: true });
onCLS(send, { reportAllChanges: true });

rating maps the raw value against good/poor thresholds automatically — alert on regressions without hardcoding numbers.

Lab tools diagnose regressions; field data (CrUX) is the actual ranking signal — they serve different purposes.

Trade-offs of the CWV framework

Pros

  • Ties user perception to measurable numbers, not vague 'feels fast' reports.
  • CrUX field data gives a 28-day real-device baseline no synthetic tool can match.
  • INP catches slow interactions that FID missed — full input-to-paint delay.
  • Three metrics cover load, interactivity, and stability — orthogonal failure modes.

Cons

  • CrUX requires enough real-user traffic; low-traffic pages get no field data.
  • Lab tools can't reproduce session-level INP across many interactions.
  • CLS is easily gamed by deferring all layout shifts to after user input.
  • LCP origin attribution is hard: hero image, TTFB, render-blocking CSS, or all three.

Interview angle

Expect questions on what each metric measures, why INP replaced FID, and how to diagnose regressions in production.

Soundbite: "FID only measured input delay; INP measures the full interaction-to-paint delay at the 98th percentile — it catches slow event handlers that FID missed."

Key terms

LCP
Time to render the largest above-the-fold image or text block; good ≤2.5 s.
INP
98th-percentile input-to-paint delay across all interactions; good ≤200 ms.
CLS
Sum of impact × distance shift scores for unexpected layout shifts; good ≤0.1.
CrUX
Chrome UX Report — 28-day rolling field data from real Chrome users, used for ranking.
LoAF
Long Animation Frame — browser entry identifying frames that exceed 50 ms.

Further reading

Search fearchitect

Jump to a topic, mode, or action.