35. Core Web Vitals: LCP, INP, CLS — measuring and fixing them
Google's Core Web Vitals are the three metrics that most directly map to perceived user experience, and are used in Search ranking:
| Metric | Measures | Good threshold | Common fixes |
|---|---|---|---|
| LCP (Largest Contentful Paint) | Time until the largest visible element (usually a hero image or heading) renders | ≤ 2.5s | Preload the LCP image, remove render-blocking CSS/JS, use SSR/SSG for above-the-fold content |
| INP (Interaction to Next Paint) | Latency from a user interaction (click/tap/keypress) to the next painted frame reflecting it | ≤ 200ms | Break up long JS tasks (§37), avoid heavy synchronous work in event handlers, use requestIdleCallback for non-urgent work |
| CLS (Cumulative Layout Shift) | Unexpected layout movement after initial render | ≤ 0.1 | Always set explicit width/height on images/embeds, reserve space for ads/late-loading content, avoid inserting content above existing content |
Measuring them in real user sessions (not just synthetic lab tests) via the
web-vitals library:
import { onLCP, onINP, onCLS } from "web-vitals";
onLCP((metric) => sendToAnalytics("LCP", metric.value));
onINP((metric) => sendToAnalytics("INP", metric.value));
onCLS((metric) => sendToAnalytics("CLS", metric.value));
Diagnosing a long task that hurts INP with the PerformanceObserver API
directly (no library needed):
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) { // "long task" threshold per the spec
console.warn(`Long task: ${entry.duration.toFixed(1)}ms`, entry);
}
}
}).observe({ type: "longtask", buffered: true });