Fix Core Web Vitals by finding which metric actually fails for real users, reproducing the relevant experience, and addressing its measured cause. For LCP, investigate when the main content becomes available and visible. For INP, inspect the work delaying a response to interaction. For CLS, identify what moves and what causes that movement. Compressing images, adding a cache plugin, or delaying scripts can each help in the right situation — none of them is a universal fix, because a page can load quickly but respond slowly, or respond quickly while its layout keeps moving.
What are Core Web Vitals?
Core Web Vitals measure three aspects of a page's real-world experience: loading, responsiveness, and visual stability. The current metrics are Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). Google evaluates the 75th percentile of real visits, separately for mobile and desktop — an average load time does not establish a pass.
- LCP — good at 2.5 seconds or less; needs improvement from just above 2.5 up to 4 seconds; poor above 4 seconds.
- INP — good at 200 milliseconds or less; needs improvement from just above 200ms up to 500ms; poor above 500ms.
- CLS — good at 0.1 or less; needs improvement from just above 0.1 up to 0.25; poor above 0.25.
These thresholds are inclusive on the good side — 2.5 seconds, 200 milliseconds, and 0.1 all meet the good target. A complete pass requires all three metrics to meet their target where sufficient data exists for each.
LCP: when prominent content appears
LCP measures when the largest eligible image or text block within the viewport finishes rendering — not the time to download every file or finish every background request. An article's text can be the LCP element just as easily as a large image, and the candidate can differ by viewport. The practical question is simple: how long does a visitor wait for the content they actually came to see?
INP: responsiveness throughout the visit
INP observes click, tap, and keyboard interactions across an entire visit — for most visits it reflects the slowest interaction, with an outlier adjustment on high-interaction visits. Scrolling and hovering aren't themselves measured as interactions, and a visit with no qualifying interaction simply has no INP value. INP replaced First Input Delay (FID) in March 2024: FID only assessed the delay before an interaction started being processed, while INP covers latency through to the next paint and considers interactions beyond the first — which is why some sites that comfortably passed FID now fail INP. INP also doesn't measure how long a complete purchase, API call, or calculation takes; a responsive loading state can give immediate feedback while the underlying operation continues, and you still need to separately confirm that operation finishes successfully.
CLS: unexpected movement
CLS measures the largest burst of unexpected layout shifts across a page's full lifecycle, not just a total accumulated during initial load — a burst groups shifts separated by less than a second, within a maximum five-second window. It's a unitless score, not a duration, and movement late in a visit counts just as much as movement during loading: a page can look stable in a short test and still shift badly when ads refresh, a widget opens, or a user scrolls into an embed.
Do Core Web Vitals affect SEO?
Google says its ranking systems use Core Web Vitals, but a good score doesn't guarantee a high ranking, and relevant content can still rank despite a weaker page experience — there's no single page-experience signal that overrides relevance. Treat performance as part of building a genuinely useful page, not a ranking eligibility gate or a substitute for content quality. There's also no defensible universal formula like "reducing LCP by one second increases traffic by a fixed percentage" — search demand, competition, content, and other simultaneous changes all affect results. If a page has many impressions and few clicks, investigate its queries and search presentation separately; a weak Core Web Vitals report alone doesn't explain why searchers aren't clicking.
Measure correctly before changing anything
Field data describes recorded real-user experiences. Lab data comes from a controlled run and helps reproduce and investigate a problem. Both are useful, but they can disagree because of differences in devices, networks, caches, visit duration, and user behavior.
- Search Console Core Web Vitals report — best for identifying affected URL groups and device categories; grouped results aren't an individual page trace.
- PageSpeed Insights field section — inspects the available URL or origin field experience; coverage and aggregation can limit specificity.
- PageSpeed Insights Lighthouse section — investigates one controlled lab load; a single run isn't a visitor distribution.
- Chrome DevTools Performance panel — traces loading and real interactions locally; your local environment may differ from real users'.
- Your own real-user monitoring — lets you investigate specific routes, releases, and interaction patterns; requires correct collection and interpretation.
Start with field evidence where it's available, and use lab tools to explain what the field data shows. If public field data isn't available for a URL, you can still test and collect your own measurements — absence of public data doesn't mean the page performs well.
Read PageSpeed Insights from the top down
PageSpeed Insights presents CrUX field data alongside Lighthouse lab diagnostics. Check whether the field result is for the exact URL or the whole origin, and whether you're viewing mobile or desktop — an origin-level result isn't a precise report for every individual page on that host. PSI can assess a page or origin using LCP and CLS alone when INP lacks sufficient data, but if LCP or CLS itself lacks sufficient data, it can't make that assessment either — read the displayed data availability rather than treating a missing INP value as an automatic failure.
A Lighthouse score is not a Core Web Vitals pass
Lighthouse's performance score is a weighted lab assessment that can vary between runs — it is not the same measurement as the field Core Web Vitals assessment. A high Lighthouse score can coexist with poor field INP if a load-only test misses slow interactions that happen later in a real visit; conversely, a constrained lab run can look slower than the actual experience most visitors have. Keep the score as a supporting indicator and record the real metric values and the cause visible in an actual trace.
Understand Search Console's URL groups and CrUX's coverage
Search Console groups similar URLs and separates mobile from desktop; a group's status reflects its worst-rated metric over the preceding 28 days, and one representative URL can differ from the aggregate. Sample multiple URLs from a group — a blog template, a product template, and an interactive tool may each have a different cause even on the same hostname. CrUX itself is drawn from eligible Chrome users and eligible pages with sufficient data — it isn't a census of every visitor or browser, and low-traffic pages may simply lack public data. Your own monitoring and CrUX can legitimately differ; compare scope and collection method before assuming one is wrong.
Establish a useful baseline
Before changing anything, record a short performance profile for each important page type: the template and a representative URL, device category, field scope and collection dates, which metric is failing, the local scenario that reproduces it (loading, search, filtering, scrolling), the component you suspect, your evidence-based hypothesis for the cause, the specific change proposed, the release identifier, and the functional checks that must still pass. Prioritize important journeys and widespread template issues — a reusable component affecting many pages is usually a better first target than a marginal improvement on one low-traffic URL. Treat an inferred cause as a hypothesis until a trace confirms it; "the chart might be slowing the filter" is a guess, a trace showing the chart's render blocking the response is evidence.
How to diagnose and fix poor LCP
Start by recording a loading trace and locating the actual LCP element at the relevant viewport — repeat on another important page in the template, not just the homepage hero. If the element is text, image compression won't touch its appearance time; if it's an image, find its network request and compare when it completes against when it visually appears.
- Break the loading path into parts — time to first byte, resource load delay, resource load duration, and element render delay — to see whether time is lost before the HTML arrives, before the image request starts, during transfer, or after the resource is already available. Fixing one part can simply expose the next bottleneck.
- Investigate a high TTFB specifically: redirects and network distance matter as much as backend execution time. Google's rough guidance is 0.8 seconds or less as a reasonable target, while being explicit that TTFB itself is not a Core Web Vital. Test cached and uncached states, less-visited routes, and different visitor geographies.
- Make the LCP resource discoverable early — expose it in the initial HTML where feasible, and avoid lazy-loading the one image that must appear immediately in the viewport. Native lazy loading is useful for suitable offscreen images; it actively delays an important visible one.
- Set fetch priority selectively on the confirmed LCP image rather than on every image — raising every image's priority defeats the purpose of flagging which one actually matters.
- Preload only when a resource would otherwise be discovered late, and match any responsive preload to the exact candidate the browser will actually select — check the network waterfall afterward, since an already early-discovered image may need no preload at all.
- Optimize the image that's actually delivered — compare responsive dimensions, compression, and real transferred bytes rather than assuming WebP or AVIF saves a fixed percentage. A small mobile display shouldn't be served the largest desktop asset.
- If the resource arrives early but still paints late, investigate style calculation, layout, script execution, and visibility rather than reaching for more image optimization — and retest LCP and CLS together after changing CSS delivery, since deferring styles the initial layout needs can cause new shifts.
<img
src="/images/guide-hero-1200.webp"
srcset="/images/guide-hero-640.webp 640w,
/images/guide-hero-1200.webp 1200w"
sizes="(max-width: 760px) 100vw, 900px"
width="1200"
height="675"
fetchpriority="high"
loading="eager"
alt="Performance trace showing an image request and its rendering time"
>
<!-- For a late-discovered responsive image, match the preload to the
exact candidate the browser will select -->
<link
rel="preload"
as="image"
imagesrcset="/images/guide-hero-640.webp 640w,
/images/guide-hero-1200.webp 1200w"
imagesizes="(max-width: 760px) 100vw, 900px"
fetchpriority="high"
>⚠️ Warning
This markup is illustrative. In a framework or CMS, inspect the actual generated output — the files must exist, the sizes must match your real layout, and the alt text must describe the real image before any of this helps.
How to diagnose and fix poor INP
Reproduce real tasks on a production-like page — open navigation, choose a filter, type in a search box, submit a form, use a calculator — both right after load and after the page has been open a while. Test the state real users actually encounter, including the consent system, analytics, ads, and personalization that a clean dev build often skips. Chrome DevTools' Performance panel records interactions and shows scripting, rendering, and timing detail; its current workflow has replaced the old standalone Web Vitals extension, which was retired in January 2025.
- Separate the interaction's delays — input delay before a handler runs, processing time inside the handler, and presentation delay before the next paint — since the largest component determines which fix actually applies. Don't assume a particular script caused the delay just because it's present on the page; find the work in the trace.
- Reduce background competition from recurring timers and startup work that occupy the main thread before an interaction even begins — ask whether polling needs its current frequency and whether initialization has to happen at that exact moment.
- Break up long, non-critical work. A task occupying the main thread for more than 50ms is a long task; yielding between separable pieces of work (scheduler.yield() where supported, with a setTimeout fallback) gives the browser a chance to respond. This can't interrupt one huge synchronous calculation — that has to be reduced, divided, or moved to a worker, and the result still needs testing for double submissions, cancellation, and errors.
- Avoid unnecessary rendering work — batch style reads and writes to avoid forced synchronous layout, and investigate unnecessary component re-renders and large lists. requestAnimationFrame is for appropriate visual scheduling, not a general way to hide expensive work from the main thread.
- Audit third-party scripts with evidence, not assumption — inventory what loads and where, then compare the same interaction in a controlled test with an optional integration disabled. A coverage report can help spot unused code, but only an actual performance trace proves which function blocked a specific click.
- Preserve useful feedback during a pending action — show an accessible pending state promptly and prevent unintended repeat submissions, while measuring the response-to-interaction and the full completion time as two separate things. Don't delay essential controls until a first click purely to improve a load-time test; that just moves the same cost into INP.
function yieldToBrowser() {
if (globalThis.scheduler?.yield) {
return globalThis.scheduler.yield();
}
return new Promise(resolve => setTimeout(resolve, 0));
}
// Place yields between separable operations, after useful feedback
// is already possible — this does not interrupt one giant synchronous
// calculation; that work has to be reduced, divided, or moved off
// the main thread entirely.How to diagnose and fix poor CLS
Identify the actual cause, not just the element that visibly moved — a paragraph highlighted in a shift trace might have moved because an image or ad above it expanded. Record a longer journey (loading, scrolling, opening controls, waiting for embeds, triggering async updates); short load-only tests routinely miss post-load instability.
- Reserve media space accurately with real width/height or an aspect ratio that matches the actual rendered design at every breakpoint — a placeholder with the wrong ratio can still shift when the real asset loads.
- Design stable ad and embed containers with realistic space reserved per breakpoint, and decide deliberately whether to preserve space for an unfilled slot or use a stable alternative — collapsing an unused slot can itself move the content beneath it.
- Control font-related reflow by trimming unnecessary font files and choosing fallback metrics compatible with your real font pair. font-display: swap makes fallback text visible sooner but doesn't by itself prevent a shift when the real font swaps in — test headings, navigation, and forms in more than one language.
- Treat consent banners and notification UI as layout components with planned space from first render, not an afterthought bolted on later.
- Account for delayed changes — CLS excludes shifts within a short window right after qualifying user input, but a much later asynchronous change isn't automatically exempt just because a user triggered it earlier. Reserve space for delayed results even when a change might fall outside what the metric counts, since it can still be a poor experience either way.
<img
src="/images/example-chart.webp"
width="960"
height="540"
loading="lazy"
alt="Chart comparing the four parts of an image-based LCP"
>
<style>
.article img { max-width: 100%; height: auto; }
.video-frame { aspect-ratio: 16 / 9; }
</style>Troubleshooting by site type
- Content/CMS sites — start with one affected article template: inspect the generated hero markup, font loading, ad containers, consent states, and plugin scripts, and check whether an optimization setting applies sitewide or only to the page you sampled. Change one setting at a time and check the page as a logged-out visitor, since admin sessions often bypass caches.
- Ecommerce — test category filters, variant selection, gallery controls, cart updates, and checkout, not just the product image. Watch inventory updates and personalized pricing for layout movement, and confirm rendering changes still show the correct item and price.
- JavaScript apps and interactive tools — separate initial delivery from interaction work; server rendering can improve early visibility, but hydration and later updates still need their own investigation. Trace realistic input sizes for calculators and charts, and preserve accuracy, keyboard behavior, and accessible feedback when dividing up work.
- Ad-heavy pages — test loaded, unfilled, and refreshed ad states over a longer session rather than only the opening seconds, and use controlled comparisons to see which specific integrations cause the work.
Validate fixes without waiting blindly
Immediately after deployment, repeat the same diagnostic scenarios on the live version under comparable device, viewport, and cache conditions, confirming the intended bottleneck actually improved and nothing important broke — don't rely solely on a changed composite score. For a faster read than public field data, use your own real-user monitoring filtered to the new release; the web-vitals library's measurement callbacks (and its attribution build) can support this, as long as collection avoids sensitive URL parameters or identifying DOM details.
Remember that the CrUX field section in PageSpeed Insights reflects a rolling, historical window, not an immediate post-deployment reading — it's updated daily, but the collection period itself stays historical, so recent local traces and release-filtered monitoring are what answer "did this work right now," while public field reports answer "has the aggregate experience improved over this window." In Search Console, the Start Tracking option after fixing a group begins a 28-day monitoring period — it doesn't trigger reindexing, and shouldn't be presented as a way to force an SEO benefit. If tracking fails, inspect the remaining group instances and production state rather than repeatedly restarting without investigating why.
Start with one failing experience
Pick an important template, confirm the measurement scope, and reproduce one real problem. Record its cause, implement a focused change, verify the page still functions correctly, and watch the relevant field distribution — chasing a perfect lab score is usually less valuable than fixing one genuinely broken journey.
💡 Tip
Core Web Vitals failures are one of the crisis types in SEOdisaster's Technical SEO chapter. The game puts you under time pressure to triage which metric is actually tanking the experience and make the right call between a quick patch and a structural fix.