Core Web Vitals are three measurements of how a page feels to real users: how quickly the main content appears, how promptly it responds to input, and how stable it is while loading. After reading this guide you will know how each metric is calculated, where to find your real-user numbers, and which fixes usually deliver the biggest improvement for each one.
The three metrics and their thresholds
Google defines "good" at the 75th percentile of page loads, segmented by mobile and desktop. That means 75 percent of visits must meet the threshold; one fast test on your office fibre connection proves little.
| Metric | Measures | Good | Needs improvement | Poor |
|---|---|---|---|---|
| LCP Largest Contentful Paint | Time until the largest visible image or text block renders | 2.5 s or less | 2.5 to 4 s | over 4 s |
| INP Interaction to Next Paint | Latency of interactions across the visit | 200 ms or less | 200 to 500 ms | over 500 ms |
| CLS Cumulative Layout Shift | Unexpected movement of visible content | 0.1 or less | 0.1 to 0.25 | over 0.25 |
INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. FID only looked at the delay of the first interaction; INP considers the responsiveness of essentially all clicks, taps and key presses during the visit and reports a value near the worst one, which is much harder to game and more representative.
Core Web Vitals are used as one of many signals in Google's page experience evaluation. Treat them as a user-experience goal first. Strong content and relevance still matter far more for rankings than moving from a "needs improvement" to a "good" score, but slow, janky pages lose visitors regardless of search.
Lab data versus field data
This distinction explains most confusion about "my score changes every time".
- Field data comes from real users. The Chrome User Experience Report (CrUX) aggregates anonymized measurements from opted-in Chrome users over a rolling 28-day window, and it is what Google's assessment uses. It reflects real devices, networks and locations. It is only available for sites or pages with enough traffic, and it lags: a fix takes weeks to fully show.
- Lab data is a synthetic test, such as Lighthouse, run on a defined device and network profile. It is repeatable and gives you diagnostics, but it cannot capture real interactions, so it cannot measure INP directly. Total Blocking Time (TBT) is the closest lab proxy for responsiveness.
Where to look:
- PageSpeed Insights shows CrUX field data at the top (when available) and a Lighthouse lab run below it. Start with the field section.
- Search Console's Core Web Vitals report groups URLs by status using the same field data.
- Chrome DevTools Performance panel lets you profile and inspect interactions locally.
- The Core Web Vitals checker and Website Speed Test give you quick checks to compare before and after.
If the field data says you fail but a lab run passes, trust the field data and investigate which users and pages are slow: often mobile users on mid-range phones, a particular template, or a particular country.
Improving LCP
LCP time breaks into four parts: time to first byte, resource load delay (when the browser discovers the LCP resource), resource load duration, and element render delay. Find which part dominates before fixing anything. DevTools and PageSpeed Insights both show the LCP element.
1. Make the server respond faster
Slow TTFB delays everything after it. See how to reduce time to first byte for caching, CDN and backend fixes.
2. Make the LCP resource discoverable early
If your LCP image is set through CSS background-image, a JavaScript-injected tag, or hidden behind a lazy-loading script, the browser finds it late. Use a real <img> in the HTML and tell the browser it is important:
<img src="/img/hero-1200.avif" width="1200" height="600"
alt="Product on a desk" fetchpriority="high"
srcset="/img/hero-600.avif 600w, /img/hero-1200.avif 1200w"
sizes="100vw">
Do not add loading="lazy" to the LCP image; lazy loading delays it. Do add it to images below the fold. For a critical image that cannot be an <img> in markup, a <link rel="preload" as="image" href="..." fetchpriority="high"> in the head helps.
3. Shrink what you load
- Use modern formats such as WebP or AVIF, with JPEG fallbacks if you need to support older browsers.
- Serve sizes matching the layout through
srcsetandsizes; sending a 3000px image to a 400px slot wastes bytes. - Compress aggressively; many hero images can lose a large share of their weight with no visible difference.
- Serve static files through a CDN; use the CDN Detector to confirm yours is in use, and the Page Weight Analyzer to see what makes up your page.
4. Remove render blockers
Large CSS and synchronous scripts in the <head> delay first paint and LCP. Inline the small amount of critical CSS, load the rest asynchronously, and add defer to scripts that do not need to run before parsing completes. Client-side-rendered pages that fetch content after JavaScript runs often have poor LCP; server-render or pre-render the above-the-fold content where possible.
5. Fonts as LCP
If the LCP element is text, a slow web font can delay it. Preload the main font file and use font-display:
<link rel="preload" href="/fonts/body.woff2" as="font" type="font/woff2" crossorigin>
@font-face {
font-family: "Body";
src: url("/fonts/body.woff2") format("woff2");
font-display: swap;
}
The crossorigin attribute is required on font preloads even for same-origin fonts, or the file will be fetched twice.
Improving INP
INP is about the main thread being free when the user interacts. An interaction has three parts: input delay (the main thread is busy), processing time (your event handlers) and presentation delay (rendering the result). Long tasks, tasks longer than 50 ms, are the usual cause.
Practical fixes, roughly in order of payoff:
- Cut third-party JavaScript. Tag managers, chat widgets, ad scripts and A/B testing tools often account for a large share of main-thread time. Inventory them with the Third-Party Script Checker, remove what you do not need, and load the rest after interaction or on idle.
- Break up long tasks. Yield to the main thread between chunks of work so input can be handled:
function yieldToMain() {
if (globalThis.scheduler && scheduler.yield) return scheduler.yield();
return new Promise(resolve => setTimeout(resolve, 0));
}
async function processItems(items) {
for (const item of items) {
handle(item);
await yieldToMain();
}
}
scheduler.yield() is not available in every browser, which is why the fallback matters.
- Keep event handlers light. Do the minimum to show a response (toggle a class, show a spinner), then defer expensive work.
- Reduce DOM size and rendering cost. Very large DOMs and expensive CSS selectors slow every update. Avoid forcing layout repeatedly by interleaving reads and writes of layout properties.
- Hydration cost. For frameworks that hydrate server-rendered HTML, ship less JavaScript, split bundles per route, and consider partial or deferred hydration for non-critical components.
- Debounce high-frequency input handlers such as scroll, resize and typing.
Because INP depends on real interactions, validate it with field data or with the DevTools interaction tracking rather than a Lighthouse run alone.
Improving CLS
CLS adds up layout shifts that are not triggered by a recent user action. Each shift scores by how much of the viewport moved and how far.
Common causes and fixes:
- Images and videos without dimensions. Always set
widthandheightattributes (or CSSaspect-ratio) so the browser reserves space before the file loads. - Ads, embeds and iframes. Reserve a container with a fixed minimum height for the slot, even if the ad sometimes does not fill.
- Late-injected content such as cookie banners and promo bars pushing the page down. Overlay them or reserve space; do not insert them above existing content after load.
- Web fonts causing reflow. Swapping fonts changes text size. Use
font-display: swapwithsize-adjustand metric override descriptors on a fallback@font-faceto match dimensions, or preload critical fonts. - Animations. Animate
transformandopacityrather than properties liketoporheightthat trigger layout.
img, video { max-width: 100%; height: auto; }
.ad-slot { min-height: 250px; }
.hero { aspect-ratio: 2 / 1; }
Note that CLS is measured across the whole page lifespan in sessions (windows of shifts), so shifts that occur on scroll or after a long delay still count.
Measuring over time
A single run proves nothing. Build a measurement habit:
- Baseline your field numbers for the most important templates (home, category, product or article, checkout).
- Add real-user monitoring by collecting the metrics yourself. The open-source
web-vitalslibrary is a small script that reports each metric:
<script type="module">
import {onLCP, onINP, onCLS} from 'https://unpkg.com/web-vitals@4?module';
const send = m => navigator.sendBeacon('/vitals', JSON.stringify({
name: m.name, value: m.value, id: m.id, page: location.pathname
}));
onLCP(send); onINP(send); onCLS(send);
</script>
- Change one thing at a time and compare the 75th percentile before and after, allowing for the 28-day window on CrUX.
- Guard against regressions by running lab checks in your deployment pipeline and alerting when budgets are exceeded.
- Watch availability too. Slow responses and outages often show up together, and an uptime monitor with response time tracking can reveal performance degradation your lab tests miss.
Next steps
- Run a check with the Core Web Vitals tool
- Compare before and after using the Website Speed Test
- Find heavy assets with the Page Weight Analyzer
- Audit scripts with the Third-Party Script Checker
- Lower server delay via reduce time to first byte
FAQ
Why do PageSpeed Insights and Search Console show different numbers?
PageSpeed Insights shows field data for the specific URL (or the whole origin if the URL lacks enough data) plus a separate lab run. Search Console groups many URLs and reports on a delayed rolling window. Differences in time window and grouping are normal.
Why does my site have no field data?
CrUX needs enough opted-in Chrome visits to report anonymously. Low-traffic pages often have none. Use lab tools for diagnostics and consider your own real-user monitoring for those pages.
Is INP the same as FID?
No. FID measured only input delay of the first interaction. INP measures full interaction latency across the visit and replaced FID as a Core Web Vital in March 2024.
Can a CDN fix my Core Web Vitals?
It helps with TTFB and asset delivery, which improves LCP, but it does not fix heavy JavaScript, unsized images or render-blocking CSS. Combine it with the front-end fixes above.
How long until improvements show up in field data?
CrUX reports a rolling 28-day window, so a fix is only fully reflected after about a month. Your own real-user monitoring will show the change much sooner.
Do I need a perfect score on every page?
No. Aim for the "good" threshold on your key templates for most real visits. Chasing perfection on rarely visited pages is poor use of time.
Get notified when this guide updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
This page is general information written by our editorial team and updated when documentation changes. Servers and providers differ, so test changes safely first. See our editorial policy and disclaimer.