Skip to content
Docs

How to improve Core Web Vitals

Improve Core Web Vitals (INP, LCP, CLS) with metric-specific fixes, field-data measurement, and Next.js/Vercel performance best practices.

Vercel

Core Web Vitals are the three metrics Google uses to score a page's loading performance, interactivity, and visual stability during real visits. A page can score well in Lighthouse and still fail the assessment, because Google scores all three from field data rather than lab tests. Largest Contentful Paint (LCP) should occur within 2.5 seconds, Interaction to Next Paint (INP) should stay at or under 200 milliseconds, and Cumulative Layout Shift (CLS) should stay at or below 0.1.

Each metric fails for its own reason and takes its own fix. Identify which one falls outside the good range, trace it to the responsible route and element, apply the fix, then confirm the result in field data rather than a lab score.

Copy link to headingWhat counts as a good Core Web Vitals score

Each Core Web Vital measures a different part of the experience against its own threshold.

Here's what each one covers:

  • Largest Contentful Paint (LCP): Measures how long the largest visible element takes to render, timed from when the page starts loading. It should complete within 2.5 seconds.
  • Interaction to Next Paint (INP): Measures how long an interaction takes, timed from input through processing to the next paint. INP should be 200 milliseconds or less.
  • Cumulative Layout Shift (CLS): Measures how far unexpected layout shifts move existing content, scored as a unitless value for the largest burst. It should stay at or below 0.1.

Google assesses each metric at the 75th percentile, so LCP, INP, and CLS each need to be in the good range for at least 75% of visits. A page passes only when all three do, and the remaining 25% of visits can still include slow or unstable experiences.

Passing won't lift a page on its own. Relevance and content quality are stronger ranking factors, and page experience only decides between pages that Google already rates as comparably relevant. INP replaced First Input Delay (FID) as the responsiveness metric in March 2024, so a page still tuned for FID is measuring the wrong thing.

Copy link to headingWhy Lighthouse and field data disagree on Core Web Vitals

Lab and field data measure different things. Lighthouse loads the page in a fixed, emulated environment and never interacts with it, so it produces no INP score. Google's Chrome User Experience Report (CrUX) collects real visits from opted-in Chrome users across the devices, networks, and locations your traffic actually comes from, and Google's assessment uses that field data exclusively.

Those two datasets can disagree sharply on the same URL. A page can post a good Total Blocking Time, or a perfect 100 lab score in PageSpeed Insights, while the CrUX field panel on the same report shows failing Core Web Vitals. Total Blocking Time points to main-thread work, but it's not an INP measurement, since the audit performs no real interactions.

Cross-browser field data is now available for two of the three metrics. LCP and INP became Baseline. Newly available when Safari 26.2 shipped on December 12, 2025, so every major browser engine now exposes the APIs needed to measure them. CLS remains a Chromium-only measurement.

Compare the lab and field panels for the same URL, then work from whichever metric fails in the field panel.

Copy link to headingHow to improve INP on Vercel

INP captures every discrete interaction across the page lifecycle, including clicks, taps, and key presses. It discards outliers and reports the longest one.

Start with route-level data rather than auditing every component. A slow search-suggestions interaction and a slow checkout action can fail for different reasons on the same site, and Speed Insights reports real-user INP by page and route, so you can tell which one you're looking at. Reproduce the slow interaction on the affected route, then find which phase holds most of its duration.

The web-vitals library splits each interaction into three phases:

  • Input delay: Other work is occupying the main thread before the handler starts, reported as inputDelay.
  • Processing duration: The handler, or work it triggers, runs long, reported as processingDuration.
  • Presentation delay: Rendering between the handler finishes and the next paint is slow, reported as presentationDelay.

Each phase takes a different fix, so read the attribution before changing code.

Copy link to headingReduce input delay

Input delay accumulates when a long task, meaning anything holding the main thread for more than 50 milliseconds, is mid-execution when the user interacts. Hydration that finished four seconds before a click contributes nothing, while a third-party analytics script firing on every scroll does.

Break long tasks into chunks and yield between them with scheduler.yield() where it's supported. Safari needs a setTimeout fallback. Deferring non-critical scripts until after the page becomes interactive frees the same thread. Yielding can raise total processing time, and net INP still improves.

Copy link to headingShorten processing duration

Expensive Document Object Model (DOM) reads and writes inside a handler extend the processing duration. Reading a layout property right after writing to the DOM forces a synchronous layout, and the browser has to recalculate before the handler can continue.

Batch DOM mutations, schedule visual updates with requestAnimationFrame, and move non-UI calculations to a web worker. For touch and scroll interactions, mark touchstart, touchmove, and wheel listeners as passive when they don't call preventDefault(), which lets scrolling start without waiting for the listener.

Copy link to headingCut presentation delay

Large DOM trees and complex CSS selectors slow the rendering pipeline between the handler finishing and the next paint. Applying content-visibility: auto lets the browser skip rendering work for off-screen content while keeping it available to find-in-page and the accessibility tree. Animating with transform and opacity avoids triggering layout or paint, so those animations run on the compositor thread.

In React applications, a large re-render after an interaction extends the same phase. React's concurrent features let useTransition mark a state update as non-urgent in React 18 and later, so the interaction paints before the heavier update lands. None of that helps when an onClick handler synchronously flushes analytics, and that work has to move out of the handler itself.

23% of mobile sites miss the good INP threshold, and those failures trace back across all three phases rather than clustering in one. For a step-by-step version of this workflow, review the INP debugging walkthrough.

Copy link to headingHow to improve LCP on Vercel

LCP breaks into four stages, and the slowest one tells you which fix to prioritize.

The stages show where the loading budget goes:

  • Time to First Byte (TTFB): The browser is waiting on the initial response, so nothing else can start.
  • Resource load delay: The browser discovers or prioritizes the LCP resource too late.
  • Resource load duration: The transfer itself is slow.
  • Element render delay: The resource has arrived, and other work prevents the browser from painting it.

Measure the stages before changing image formats or infrastructure, since a fix aimed at the wrong stage barely moves the number.

Copy link to headingLower TTFB with the CDN and fluid compute

Serving requests through Vercel CDN puts that first response closer to the visitor, across more than 126 points of presence (PoPs), with request-time code running in the nearest of 20 compute-capable regions behind them. Traffic moves between the two over a private network.

On request-rendered routes, TTFB reflects server work rather than network distance. Fluid compute reuses warm instances and runs multiple invocations on a single instance, which reduces cold starts on the routes that render per request.

Copy link to headingStart the LCP resource earlier

The browser discovers the LCP resource late when it has to download HTML, parse CSS, and only then find the image reference. A preload hint in the document head starts the fetch immediately and fetchpriority="high" upgrades its priority. Keep the number of preload hints small, since competing hints contend for the same bandwidth.

External stylesheets block rendering by default, so the browser won't paint until blocking CSS in the head is downloaded and parsed. Inline the critical CSS for above-the-fold content and load the rest asynchronously. Never lazy-load the LCP image, as it is designed to delay the fetch.

Copy link to headingShrink the LCP file

AVIF compresses roughly 50% better than JPEG, with slightly less browser coverage than WebP. Vercel Image Optimization handles the format negotiation and caches each transformation, and the Next.js Image component applies responsive sizing so a phone doesn't download a desktop-width file.

Verify the same route after each change. If TTFB falls and resource load delay stays high, move to earlier discovery, and if the transfer is still slow, sizing and format negotiation are the next targets. Vodafone improved LCP by 31% and recorded an 8% increase in sales, a 15% improvement in lead-to-visit rate, and an 11% improvement in cart-to-visit rate.

Copy link to headingHow to improve CLS on Vercel

CLS reflects the largest burst of unexpected layout shifts rather than a lifetime sum. The session window is capped at 5 seconds, and counts shifts less than a second apart.

Each shift multiplies the impact fraction by the distance fraction, so one large shift can consume the budget alone. A banner that pushes the whole viewport down by 22% of its height has an impact fraction of 1.0 and a distance fraction of 0.22, scoring 0.22 on one shift and clearing twice the budget. Shifts within 500 milliseconds of a user interaction are excluded, since a menu opening shouldn't count against the score.

Replay the largest burst and find what moved the existing content. The usual causes are media without dimensions, content injected after load, fonts that swap at a different size, and layout changes at hydration.

Copy link to headingReserve space before content renders

Images and videos without width and height give the browser nothing to reserve, so everything below them shifts once the file loads. Set explicit width and height on <img>, <video>, and <iframe> elements, or use CSS aspect-ratio for responsive media. For content whose size is unknown at render time, such as a third-party widget or an injected banner, a sensible min-height bounds the shift as the container grows.

Copy link to headingMatch font metrics

With font-display: swap, text renders in a fallback font and re-renders when the custom font arrives, reflowing surrounding content when the metrics differ. Matching fallback font metrics with size-adjust, ascent-override, descent-override, and line-gap-override makes the swap invisible. The Next.js Font module handles the preloading and metric matching at build time.

Copy link to headingPrevent shifts at hydration and on back navigation

Toggling navigation with window.innerWidth inside a useEffect snaps the layout after hydration, so use CSS @media queries instead and keep both DOM nodes in the initial HTML. Rendering a skeleton of the same dimensions on the server reserves space before client data arrives.

Back and forward navigation can avoid new shifts entirely when the browser restores the page from bfcache, because the layout is already final. Pages become ineligible when they attach unload listeners, so check eligibility in Chrome DevTools before release.

After applying a fix, replay the same state on a Preview Deployment with the Vercel Toolbar Layout Shift tool and check the whole session window, since a later insertion can still become the largest burst.

Copy link to headingCore Web Vitals benchmarks for retail storefronts

Storefronts concentrate each metric on a different template, which is why a sitewide average hides the problem.

On a product detail page, the LCP element is typically the main product image, so the 2.5-second budget splits between TTFB and that file's own load duration. Responsive AVIF sizing is what keeps that second half of the budget in range.

Checkout is the likeliest place for a storefront's INP score to be set, since those actions are both frequent and heavy on handler work. Validate the cart and payment handlers separately, because each one can fail at a different phase.

Promotional banners produce the typical storefront CLS regression. A large banner dropped in above the fold without reserved space can consume or exceed the 0.1 budget on its own, so audit those injections before a traffic peak. For the wider operational checklist around peak events, see the Black Friday preparation guide.

Speculative loading can also shorten navigation between storefront pages by fetching the likely next page before the click. Shopify recorded a 228ms desktop LCP gain after moving from a conservative to a moderate speculation configuration, and a smaller 24ms gain on mobile.

Confirm that each change improves the affected route without adding a cost elsewhere in the checkout flow.

Copy link to headingHow to verify a Core Web Vitals fix in production

Google assesses Core Web Vitals using field data from real visits, so a lab result never confirms a fix on its own.

The main measurement options differ in the data they return and the questions they answer:

ToolData typeReal-user CWV dataBest forNot best for
LighthouseLab onlyNoDiagnosing loading and main-thread issuesReal-user INP or Google's assessment
PageSpeed Insights (lab panel)LabNoScore checks during developmentReal-user measurement
web-vitals libraryFieldYesFirst-party reporting into your own analyticsGoogle's ranking dataset
Chrome User Experience Report (CrUX)Field (Google)YesThe dataset Google assesses, on a 28-day rolling windowImmediate post-deployment feedback
PageSpeed Insights (field panel)Field (CrUX)YesA single-URL CrUX readImmediate post-deployment feedback
Speed InsightsField (real user monitoring)YesPer-page and per-route real-user breakdownsRepresenting Google's ranking dataset

Speed Insights collects real-user LCP, INP, and CLS from every deployed environment and filters by route, device type, percentile, time range, and country, defaulting to the P75 view Google assesses against. Route-level detail turns a site-wide number into an actionable one. An INP of 240ms tells you visitors hit a slow response, and attributing it to the search-suggestions handler on /products tells the team where to start.

Waiting on CrUX before checking real user monitoring delays feedback by up to a month, so verify the affected route first, then watch CrUX confirm it.

Copy link to headingNext steps

Once all three metrics pass at the 75th percentile, hold them there through each release. Start a new Vercel project and turn on Speed Insights to track LCP, INP, and CLS for real visits by page and route, or browse the templates for a configuration that already applies image, font, and rendering defaults.

Copy link to headingRead more

Copy link to headingFrequently asked questions

Why do Core Web Vitals fail when a page feels fast?

Google assesses Core Web Vitals at the 75th percentile of real visits, across devices, networks, and routes you may never test yourself. One fast visit says nothing about the other 74%. A page can also render its main content fast and still respond slowly to interactions or shift content during load.

How long does a Core Web Vitals fix take to show up?

Lab tools reflect a deployed change immediately, and real user monitoring starts collecting new visits as soon as the fix ships. CrUX uses a 28-day rolling window updated daily, so Google's field assessment moves gradually as newer visits replace older ones. Use route-level monitoring while that window catches up.

Are all three Core Web Vitals equally important for ranking?

No. Google assesses the three independently, with no weighting or averaging between them, so one failing metric fails the page however strong the other two are. Push the metric that sits outside its threshold rather than improving one that already passes.

Should you fix mobile or desktop Core Web Vitals first?

Mobile first. The 2025 Web Almanac reports 97% of desktop websites have good INP against 77% on phones, a 20-point gap that traces to slower CPUs, less memory, and slower networks. Mobile is where most regressions surface, so start with mobile field data for the failing route.

Does a good Lighthouse score mean a page passes Core Web Vitals?

No. Lighthouse runs one simulated load without interacting, so it reports no INP score and can't represent the devices and networks in field data. A perfect lab score can sit alongside failing Core Web Vitals in CrUX. Use Lighthouse to diagnose loading issues, then confirm with field data.