webmasterhelper

Performance · 9 minute read

Core Web Vitals without the guesswork

Three metrics, measured on real visitors at the 75th percentile, over a rolling 28 days. Your lab score is a model of them, not a measurement.

MetricGoodNeeds workPoorWhat it measures
LCP
Largest Contentful Paint
≤ 2.5 s2.5–4.0 s> 4.0 sWhen the largest block of text or the main image finishes rendering. A proxy for "the page looks ready".
INP
Interaction to Next Paint
≤ 200 ms200–500 ms> 500 msThe worst interaction latency across the visit — tap or key press to the next frame drawn. Replaced FID in March 2024 and is much harder to pass.
CLS
Cumulative Layout Shift
≤ 0.10.1–0.25> 0.25How much visible content moves without the user causing it. Unitless; the product of shift distance and the fraction of the viewport affected.

Fixing LCP

Break the number into four parts and find which one dominates — guessing here wastes the most time.

PhaseTypical causeFix
Time to first byteSlow application or no cachingPage caching, a CDN, a faster database query. Target under 800 ms.
Resource load delayThe LCP image is discovered latePut it in the HTML rather than CSS or JS, add fetchpriority="high", and never loading="lazy" on it.
Resource load timeThe image is too largeAVIF or WebP, correct dimensions, srcset. This is usually where the seconds are.
Element render delayRender-blocking CSS or JS, or a font swapInline critical CSS, defer scripts, use font-display: swap and preload the font file.

Fixing INP

INP is a main-thread problem. Long tasks block the next frame, and no amount of image optimisation helps.

Fixing CLS

Measuring the real thing yourself

// The same library Chrome uses, ~2 KB. Report to your own endpoint.
import { onLCP, onINP, onCLS } from 'web-vitals';

const send = (metric) => {
  navigator.sendBeacon('/vitals', JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    path: location.pathname,
  }));
};

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

Collecting your own field data gets you per-page numbers immediately, rather than waiting 28 days for a rolling average over URLs that may not have enough traffic to appear at all.