Core Web Vitals explained: how to improve LCP, INP and CLS

Core Web Vitals are three Google metrics that measure real-user experience: Largest Contentful Paint (LCP) for loading, Interaction to Next Paint (INP) for responsiveness and Cumulative Layout Shift (CLS) for visual stability. A page passes when at least 75% of real visits have an LCP of 2.5 seconds or less, an INP of 200 ms or less and a CLS of 0.1 or less. You improve them by speeding up the server response and the main image, keeping long JavaScript tasks off the main thread, and reserving space for everything that loads late.
The three Core Web Vitals and their thresholds
Each metric has three bands. Google classifies a page, or a group of similar pages, by the value at the 75th percentile of page views, assessed separately for mobile and desktop.
| Metric | What it measures | Good | Needs improvement | Poor |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | Time until the largest image or text block in the viewport renders | ≤ 2.5 s | 2.5–4.0 s | over 4.0 s |
| INP (Interaction to Next Paint) | Delay from a click, tap or key press to the next frame, for nearly the slowest interaction | ≤ 200 ms | 200–500 ms | over 500 ms |
| CLS (Cumulative Layout Shift) | How much visible content moves unexpectedly, scored by the worst burst of shifts | ≤ 0.1 | 0.1–0.25 | over 0.25 |
INP replaced First Input Delay (FID) in March 2024. FID measured only the wait before the first interaction's handler started; INP covers every interaction in the visit, including processing and rendering time, so many pages that passed FID fail INP. The web.dev Core Web Vitals overview has the current definitions.
Why the 75th percentile matters
At the 75th percentile, one visit in four can miss the threshold and the page still passes, but averages cannot save you: if more than a quarter of visitors use slow phones or sit far from your server, their experience decides the result. That is how a page that loads in 1.2 seconds at the office still shows "Needs improvement" in Search Console.
Field data vs lab data: CrUX, PageSpeed Insights and Search Console
Field data comes from real Chrome users who opted in to share usage statistics. Google aggregates it in the Chrome UX Report (CrUX) over a rolling 28-day window, and this is the data used for search. Lab data comes from one scripted page load, such as a Lighthouse run, with fixed device and network settings.
| Tool | Data type | Real INP? | Best used for |
|---|---|---|---|
| CrUX (API and BigQuery) | Field, 28-day rolling | Yes | Origin, URL and device trends; country data in BigQuery |
| PageSpeed Insights | CrUX field data plus one Lighthouse lab run | Field section only | Quick pass/fail check and diagnostics for one URL |
| Search Console Core Web Vitals report | Field, grouped by similar URLs | Yes | Finding failing page templates and validating fixes |
| Lighthouse | Lab, one simulated load | No; Total Blocking Time is the proxy | Debugging LCP sub-parts, long tasks and shifts |
If PageSpeed Insights reports too little real-user data, collect your own with Google's open-source web-vitals library; its attribution build also reports which element was the LCP and which interaction was slow.
How to improve Largest Contentful Paint (LCP)
The LCP element is usually a hero image, a video poster or a large heading. Chrome stops reporting new LCP candidates once the user interacts, so LCP reflects the initial load. The reliable fix is to split it into four sub-parts and attack the largest, as described in Google's LCP optimization guide.
The four LCP sub-parts
- Time to first byte (TTFB): from the start of navigation until the first byte of HTML arrives, including redirects, DNS lookup, TCP and TLS setup and server processing.
- Resource load delay: the gap between TTFB and the moment the browser starts downloading the LCP resource. It grows when the image is discovered late, for example from CSS, JavaScript or a lazy-loading library.
- Resource load duration: the time to download the LCP image itself (zero for a text element).
- Element render delay: the time from download complete to the element actually painting, usually spent waiting on render-blocking CSS, JavaScript or fonts.
As a rule of thumb from web.dev, a well-optimized page spends roughly 40% of LCP on TTFB, 40% on load duration and under 10% each on load delay and render delay. Large load or render delays are pure waste and usually the cheapest to remove.
Fixes for each LCP sub-part
- TTFB: cache rendered HTML, serve it from a CDN edge near users, remove redirect chains and enable TLS 1.3. Our guide to reducing TTFB covers the server side.
- Load delay: put the LCP image in the HTML as a normal
<img>withfetchpriority="high", never lazy-load it, and preload it if it is a CSS background. - Load duration: serve AVIF or WebP at the displayed size with
srcset, from the same CDN as the page. See our image optimization guide. - Render delay: inline critical CSS, defer non-critical JavaScript and avoid rendering the main content client-side. Our guide to eliminating render-blocking resources shows how.
<!-- LCP hero image: in the HTML, prioritized, sized, not lazy -->
<img src="/img/hero-1200.avif"
srcset="/img/hero-800.avif 800w, /img/hero-1200.avif 1200w"
sizes="(max-width: 800px) 100vw, 1200px"
width="1200" height="630" fetchpriority="high" alt="Product dashboard">
<!-- Only when the LCP image is a CSS background -->
<link rel="preload" as="image" href="/img/hero-1200.avif" fetchpriority="high">
How to improve Interaction to Next Paint (INP)
INP observes every click, tap and key press during a visit and reports one of the slowest; on busy pages it ignores the single worst interaction for every 50, so one outlier does not dominate. Each interaction has three phases: input delay (waiting for the main thread to be free), processing duration (your event handlers) and presentation delay (style, layout and paint of the next frame).
What causes poor INP
- Long tasks: any main-thread task over 50 ms blocks input. A tap during a 300 ms task waits for it to finish before your handler starts.
- Heavy JavaScript: large bundles, framework hydration and expensive re-renders after state changes keep the main thread busy.
- Third-party scripts: tag managers, A/B testing tools, chat widgets, ad scripts and session recorders run long tasks you do not control.
- Expensive rendering: huge DOMs and layout reads right after style changes slow the presentation phase.
Yield to the main thread and break up long tasks
Do the urgent work first (visual feedback), yield so the browser can paint, then do the rest. scheduler.yield() gives control back while keeping your continuation at the front of the task queue; feature-detect it and fall back to setTimeout where it is missing.
function yieldToMain() {
if (globalThis.scheduler?.yield) return scheduler.yield();
return new Promise(resolve => setTimeout(resolve, 0));
}
// Break a long loop into ~50 ms chunks
async function processAll(items) {
let deadline = performance.now() + 50;
for (const item of items) {
processItem(item);
if (performance.now() > deadline) {
await yieldToMain();
deadline = performance.now() + 50;
}
}
}
button.addEventListener('click', async () => {
button.classList.add('is-loading'); // feedback paints first
await yieldToMain();
await saveAndRecalculate(); // heavy work runs after the paint
});
Beyond yielding, the biggest wins come from running less code: remove unused third-party tags, load the rest with defer or after the first interaction, code-split by route and move pure computation to a Web Worker. Nobody clicks during a Lighthouse run, so use Total Blocking Time as the lab proxy and confirm with field data or the DevTools Performance panel while you interact. The web.dev INP guide has more patterns.
How to fix Cumulative Layout Shift (CLS)
A layout shift happens when a visible element changes position between frames without a recent user input. Each shift is scored as impact fraction times distance fraction; shifts less than one second apart are grouped into a session window of up to five seconds, and CLS is the score of the worst window. Shifts within 500 ms of a tap, click or key press are excluded, so opening an accordion does not count.
Common causes of layout shift
- Images, videos and iframes without
widthandheightattributes or a CSSaspect-ratio. - Ads, embeds, cookie banners and promo bars injected above existing content.
- Web fonts that swap in with different metrics from the fallback font, reflowing every line of text.
- Animations that change
top,left,widthorheightinstead oftransform.
Fixes that keep the layout stable
/* Reserve space before late content arrives */
img, video { max-width: 100%; height: auto; } /* keep width/height in the HTML */
.embed-video { aspect-ratio: 16 / 9; width: 100%; }
.ad-slot-top { min-height: 250px; } /* height of the most common ad */
/* Match fallback font metrics to the web font (tune per font) */
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 107%;
ascent-override: 90%;
}
body { font-family: "Inter", "Inter Fallback", sans-serif; }
Also preload the one or two font files used above the fold, consider font-display: optional where a fallback font is acceptable, animate with transform instead of layout properties, and show cookie banners as fixed overlays instead of pushing the page down.
How multi-region TTFB affects field LCP for distant visitors
TTFB is the first sub-part of LCP and the one most affected by distance. A new connection costs DNS, TCP and TLS round trips plus at least one for the HTML request, so a visitor 10,000 km from your server can spend a large share of the 2.5-second budget before the browser sees any HTML. CrUX blends all visitors into one 75th-percentile value, so slow regions can push a page into "Needs improvement" even when it is instant near your data center. Our guide on why websites are slow in other countries explains the physics.
You can see this when you run a free 8-region speed test. Global Website Speed Test loads your page in headless Chromium from Seoul, Tokyo, Singapore, Sydney, Virginia, Oregon, Frankfurt and London at once. Read the results like this:
- Origin vs far regions: the region marked "origin" has the fastest TTFB and is usually closest to your server. The gap to the farthest regions approximates what distant visitors add to LCP.
- Rebuild Chrome's TTFB: our TTFB column is server wait only, from request sent to first byte. Add the DNS, TCP and TLS segments of the timeline to approximate the TTFB sub-part that Chrome counts in LCP.
- First vs repeat visit: the repeat visit uses the browser cache, like a returning visitor. If only first visits are slow in far regions, cache static files longer; if both are slow, the HTML itself is the bottleneck.
- Load time vs TTFB: a wide gap in distant regions means images and scripts also come from far away, which inflates LCP load duration.
For example, a site hosted only in Virginia typically shows a few milliseconds of TCP and TLS from the Virginia probe but around 400 ms from Sydney (one round trip of roughly 200 ms each for TCP and TLS 1.3), plus another round trip inside server wait. Sydney visitors lose 0.6 seconds or more before the HTML arrives, and pay the distance again when the LCP image downloads from the same server. A CDN that caches HTML and images at the edge closes most of that gap.
These are lab measurements from data-center connections, not CrUX field data, and real visitors add mobile latency and slower devices on top. Use them to locate where LCP time goes by region, then confirm in CrUX; the methodology page explains what each probe measures.
Do Core Web Vitals affect Google rankings?
Yes, but modestly. Google's Search documentation says Core Web Vitals are used by its ranking systems as part of page experience, and it is equally clear that relevance comes first: a highly relevant page with mediocre vitals can outrank a fast page that answers the query less well. Treat them as a signal that helps when competing pages are similarly useful, not a shortcut to the top; the stronger reason to fix them is that visitors abandon slow, janky pages. A practical order of work:
- Start with the failing Search Console URL group that has the most traffic, mobile first.
- For LCP, find the largest sub-part with DevTools or the
web-vitalsattribution build and fix it. - For INP, find the long tasks behind slow interactions, then yield, defer or delete code.
- For CLS, give every late-loading element dimensions or reserved space.
- Re-test from all regions after each deploy and allow 28 days for CrUX to catch up.
Frequently asked questions
What are the three Core Web Vitals?
Largest Contentful Paint (LCP) measures loading, Interaction to Next Paint (INP) measures responsiveness and Cumulative Layout Shift (CLS) measures visual stability. A page passes when at least 75 percent of real visits have an LCP of 2.5 seconds or less, an INP of 200 ms or less and a CLS of 0.1 or less.
What replaced First Input Delay (FID)?
Interaction to Next Paint (INP) replaced First Input Delay as a Core Web Vital in March 2024. INP measures the full latency of interactions throughout the visit, including handler processing and the next paint, not just the input delay of the first interaction.
Why are my PageSpeed Insights lab and field results different?
The field section shows 28 days of real Chrome user data from the Chrome UX Report at the 75th percentile, across real devices, networks and locations. The lab section is a single simulated Lighthouse load with fixed throttling, so it is useful for debugging but it is not the data Google uses to assess your page.
Do Core Web Vitals affect Google rankings?
Yes, but modestly. Google uses Core Web Vitals as part of its page experience signals, while relevance and content quality carry far more weight. Good scores help most when competing pages are otherwise similarly useful.
How long do Core Web Vitals fixes take to show up?
The Chrome UX Report uses a rolling 28-day window, so improvements appear gradually and are fully reflected about four weeks after you deploy. Search Console validation of a fixed URL group also takes about 28 days.


