Core Web Vitals: LCP, INP, and CLS Explained
LCP, INP, and CLS: what each metric measures, Google's current thresholds, and how to fix the most common issues to pass CrUX.
The PageSpeed Insights report came back at 47/100, with three red flags: LCP, INP, CLS. The client wanted to know what to fix. The dev team wanted to know where to start. Nobody was sure what the three metrics actually measured — only that they were red and that it was hurting search rankings.
This post is here to settle that once and for all.
What Are Core Web Vitals
Core Web Vitals is the set of metrics Google uses to evaluate real user experience on a page. These are not synthetic lab benchmarks — they're calculated from actual user data collected by Chrome and stored in the CrUX (Chrome User Experience Report).
As of 2026, the set consists of three metrics:
- LCP — Largest Contentful Paint
- INP — Interaction to Next Paint
- CLS — Cumulative Layout Shift
Each one measures a different dimension of experience: loading, interactivity, and visual stability.
Google evaluates each metric at the 75th percentile of real sessions. That means 75% of your visitors need to have a "good" experience for the page to pass. It's not the average — it's the 75th percentile. A site that's fast for most users but slow for those on mobile connections can still fail.
LCP: Largest Contentful Paint
LCP measures how long it takes for the largest visible element in the viewport to render.
Thresholds:
- Good: ≤ 2.5 seconds
- Needs improvement: 2.5 s – 4.0 s
- Poor: > 4.0 seconds
The "largest element" is typically the hero image, a large text block, or a video thumbnail. The browser automatically identifies the largest element in the viewport during load.
What Affects LCP
LCP has four main causes:
Slow server response time (high TTFB) — before the browser can render anything, it needs the HTML. A TTFB above 600 ms already compromises LCP.
Render-blocking CSS and JS — resources that block rendering on the critical path delay the first paint.
No fetch priority on the LCP image — the browser typically discovers the LCP image late (only after parsing HTML and CSS). Without
fetchpriority="high", it competes with other resources.Unoptimized image — large file size + wrong format = slow download.
How to Improve LCP
<!-- add fetchpriority to the LCP image -->
<img
src="/hero.webp"
fetchpriority="high"
loading="eager"
alt="Description"
/>
Beyond that: serve images in WebP or AVIF, use a CDN with aggressive caching, and avoid redirect chains during initial load. If the LCP element is text, make sure the font uses font-display: swap and is preloaded:
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
INP: Interaction to Next Paint
INP is the newest metric — and the one most commonly failed. In March 2024, Google officially replaced FID (First Input Delay) with INP as a Core Web Vital.
Why replace FID? FID only measured the delay until the first user input was processed. INP is broader: it measures the latency of all interactions in a session (clicks, taps, key presses), and reports the worst case (with a small buffer for extreme outliers).
Thresholds:
- Good: ≤ 200 ms
- Needs improvement: 200 ms – 500 ms
- Poor: > 500 ms
In 2026, INP is the metric with the highest failure rate: 43% of sites still exceed the 200 ms threshold. That's not a coincidence — it exposes real main thread work that FID was hiding.
How INP Is Calculated
An INP interaction has three phases:
- Input delay — time between the user event and when the event handler starts
- Processing time — handler execution time
- Presentation delay — time until the next frame is painted by the browser
INP is the worst percentile of these interactions across the entire session, ignoring only the most extreme outliers.
What Causes High INP
- Long tasks on the main thread — any task above 50 ms blocks the browser. If a click event triggers a long task, INP spikes.
- Heavy hydration in frameworks — React, Vue, Angular: the hydration process can block the main thread for hundreds of milliseconds on complex pages.
- Synchronous event handlers doing heavy work — parsing data, DOM manipulation, looping over large lists.
How to Improve INP
The main approach is breaking long tasks with scheduler.yield() or setTimeout(0):
button.addEventListener('click', async () => {
// urgent work — needs to respond to the user
updateUI();
// yield the main thread before continuing
await scheduler.yield();
// heavy work that can wait
processLargeDataset();
});
Another technique is moving heavy work to a Web Worker, taking it off the main thread entirely. Anything that doesn't need to touch the DOM is a candidate.
For frameworks, the key is not doing unnecessary work in event handlers — defer calculations that don't impact the immediate visual.
CLS: Cumulative Layout Shift
CLS measures visual instability — how much elements on the page shift unexpectedly while the user is interacting.
Thresholds:
- Good: ≤ 0.1
- Needs improvement: 0.1 – 0.25
- Poor: > 0.25
The CLS value is dimensionless — it's the sum of all "layout shift scores" during the session. Each shift is calculated as impact fraction × distance fraction, where impact fraction is the affected area and distance fraction is how far the element moved (as a fraction of the viewport).
Common Causes of CLS
- Images without declared dimensions — the browser doesn't reserve space before loading the image, so the layout shifts when it arrives.
- Dynamically injected ads and embeds — ad network banners appearing at the top push all content down.
- Web fonts causing FOUT — when the font loads and replaces the fallback, text size may change and displace elements.
- Elements injected above existing content — cookie banners, toasts, overlays that trigger reflow.
How to Fix CLS
/* always declare width and height on images */
img {
width: 100%;
height: auto;
aspect-ratio: 16 / 9; /* reserve the space */
}
For ads, reserve space with min-height before the ad loads. For fonts, font-display: optional eliminates FOUT entirely (but the font may not show if loading is slow). font-display: swap is the middle ground — fallback font is visible, but swaps when the web font arrives.
@font-face {
font-family: 'Inter';
font-display: swap; /* or optional, depending on FOUT tolerance */
}
How to Measure Core Web Vitals
There are two distinct contexts: lab data (synthetic) and field data (real users).
Lab data (simulation):
- Lighthouse (DevTools, CLI, or PageSpeed Insights)
- WebPageTest
Field data (real users):
- PageSpeed Insights → "Field Data" tab (pulls from CrUX)
- Google Search Console → "Core Web Vitals" report
web-vitalslibrary directly in your code
# audit a URL via CLI
npx lighthouse https://example.com --output html --output-path report.html
The difference matters: lab data uses a simulated device and connection under controlled conditions. Field data reflects what your actual users experience. You can pass in Lighthouse and fail in CrUX — usually because your real users are on slower devices or worse mobile connections than the simulation assumes.
To monitor production, Google's official web-vitals library sends metrics to any analytics endpoint:
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP(metric => sendToAnalytics(metric));
onINP(metric => sendToAnalytics(metric));
onCLS(metric => sendToAnalytics(metric));
Do Core Web Vitals Actually Affect Rankings?
Yes — but with nuance. Google confirmed Core Web Vitals as a ranking factor in 2021, and INP became an official factor alongside the FID replacement in March 2024.
The key word is "tiebreaker." For two pages with similar content, authority, and relevance, the one that passes Core Web Vitals has the edge. It's not like backlinks or content relevance — it won't compensate for a low-quality page. But in a competitive niche where the top results have similar quality, it can be what separates position 3 from position 1.
The more direct impact is on bounce rate. A site with a 4-second LCP or a layout that keeps jumping loses users before any ranking consideration even applies.
Frequently Asked Questions
CrUX doesn't have enough data for my site. What happens?
Google uses a hierarchical fallback: URL → similar URL group → entire domain. If your site has low traffic and per-URL data is insufficient, Google uses domain-level data. For very small sites, Core Web Vitals may not directly affect rankings due to lack of data.
My site passes Lighthouse locally but fails in Search Console. Why?
Lab data and field data are different contexts. Lighthouse simulates a Moto G Power on a slow 4G connection — a specific scenario. Your real users may be on even slower devices, worse connections, with other tabs open. The CrUX 75th percentile captures those cases. Trust field data more for product decisions.
Do INP and FID measure the same thing?
No. FID measured only the delay until the first input was processed — and only the initial delay, not execution time. INP measures all interactions in the session, including handler execution time and time to the next paint. A site could have excellent FID but a frozen main thread in subsequent interactions. INP exposes that.
Can I improve INP without changing my framework?
Yes. The fastest win is usually in event handlers — find which ones are doing heavy work using the DevTools Performance panel (look for "Long Animation Frame" or "Long Task" triggered by interactions), then move the heavy work to after a yield or into a Worker.
The 75th Percentile Is the Number to Watch
Core Web Vitals aren't a one-time audit — they're a continuous signal. A deploy that introduces an LCP image without fetchpriority, a component doing heavy work on click, a poorly implemented cookie banner — any of these can fail a metric that was previously passing.
The practical path is instrumenting web-vitals in production, shipping the data to an analytics backend, and setting alerts when the 75th percentile climbs above the threshold. Occasional Lighthouse runs help catch regressions before deploy — but field data is what tells you what's actually happening with your users.
For auditing and generating your page's meta tags and og:image while optimizing for LCP, the Quick Tools Meta Tag Generator saves time over writing and checking every tag by hand.
Note: the editorial content ends here. What follows is a reference to the project described in this post.
About Quick Tools
Quick Tools is a platform of developer and productivity utilities, running as a static site. Visit quickeasy.tools.
- 01 What DevOps Actually Is Beyond the Tools DevOps isn't a pipeline or a job title. It's shared ownership between the people who write code and the people who run it in production — and why most teams get it wrong.
- 02 Relational vs NoSQL: How to Actually Choose An honest comparison of relational and NoSQL databases — real tradeoffs on consistency, schema, and scale, with a clear default recommendation.