How to Fix Core Web Vitals
How to Fix Core Web Vitals & Pass Google's Page Experience
Core Web Vitals are Google's three official performance metrics. They measure how fast your page loads, how quickly it responds to user input, and how stable the layout is while loading.
If any one of them is failing, you are likely losing rankings, losing users, and losing conversions — even if your content is excellent.
This guide covers everything: what each metric measures, how to diagnose which one is failing, and exactly how to fix it. Platform-specific fixes for WordPress, Shopify, and Next.js are included at the end.
What are Core Web Vitals?
Core Web Vitals are three specific metrics Google uses to measure real-world user experience. They became official ranking signals in 2021 as part of Google's Page Experience update.
The three metrics are:
- LCP (Largest Contentful Paint) — measures loading performance. How long does it take for the largest visible element on screen to fully load?
- INP (Interaction to Next Paint) — measures interactivity. How long does it take for the page to visually respond after a user clicks, taps, or types?
- CLS (Cumulative Layout Shift) — measures visual stability. How much does the page content unexpectedly jump around while loading?
Here are the scoring thresholds for each:
| Metric | Good | Needs Work | Poor |
|---|---|---|---|
| LCP | ≤ 2.5s | 2.5s – 4.0s | > 4.0s |
| INP | ≤ 200ms | 200ms – 500ms | > 500ms |
| CLS | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |
Google measures all three at the 75th percentile of real users. That means 75% of your visitors need to have a good experience — not just the average visitor.
Do Core Web Vitals affect rankings?
Yes — but they are a tiebreaker, not the primary ranking factor. When two pages cover the same topic with similar content quality, the one with better CWV scores will rank higher.
Beyond rankings, poor CWV directly hurts your business. A 100ms delay in page response reduces conversions by around 7%. High CLS causes users to click the wrong thing and leave. Poor INP makes your site feel broken even when it technically works.
How to diagnose your Core Web Vitals
Before you fix anything, find out exactly which metric is failing and on which pages. Guessing wastes time.
This is the most important concept before you start:
- Lab data is a simulated test. Tools like Lighthouse and PageSpeed Insights run a controlled test from a fixed location, on a fixed device, on a fixed connection. Great for debugging — but not what Google scores you on.
- Field data (CrUX — Chrome User Experience Report) is collected from real users visiting your site. Actual devices, actual connection speeds, actual geography. This is what Google uses for rankings.
Always check field data first to confirm you have a real problem. Then use lab data to diagnose why.
Step 1: Check Google Search Console
- Go to Google Search Console → Experience → Core Web Vitals
- Check both Mobile and Desktop reports — Google scores them separately
- Click into "Poor URLs" to see which pages are failing
- Note which metric is flagged — this tells you where to focus
Step 2: Run PageSpeed Insights
Paste a failing URL into pagespeed.web.dev. You will see two sections:
- "Discover what your real users are experiencing" — field data from CrUX. Your actual scores.
- "Diagnose performance issues" — Lighthouse lab data. Lists specific issues with estimated time savings for each fix.
The Diagnostics section maps every issue to the metric it affects. This is where you will spend most of your time.
Core diagnostic tools
| Tool | Best For |
|---|---|
| PageSpeed Insights | Best starting point — lab + field data together |
| Google Search Console | Real-user field data across all your URLs |
| Chrome DevTools Performance panel | Deep debugging for INP long tasks and CLS |
| Web Vitals Chrome extension | Live CWV overlay as you browse your site |
| Lighthouse CLI | Automated testing in CI/CD pipelines |
Pro tip: Do not chase a perfect Lighthouse score. A score of 90+ in the lab means nothing if your field data is still poor. Fix the field data first.
How to fix LCP (Largest Contentful Paint)
LCP is the most commonly failing Core Web Vital. The target is 2.5 seconds or less. Most LCP problems come down to one of four root causes.
What counts as the LCP element?
PageSpeed Insights will show you exactly what your LCP element is. The most common ones are:
- Hero images (by far the most common — especially on ecommerce and landing pages)
- Large above-the-fold text blocks
- Background images loaded via CSS
- Video poster images
The 4 phases of LCP — where time is being lost
Google breaks LCP into four phases. Knowing which phase is slow tells you exactly which fix to apply:
- Time to First Byte (TTFB) — server is responding slowly
- Resource load delay — browser is not discovering the LCP image early enough
- Resource load time — the image itself is too large or too slow to download
- Element render delay — JavaScript is blocking the element from painting
Fix 1: Improve your server response time (TTFB)
If your TTFB is above 800ms, your server is the bottleneck. Everything else you do will be limited by how long the server takes to respond.
- Use a CDN. A Content Delivery Network serves your pages from servers physically closer to each user. This alone can cut TTFB by 50–70% for users far from your origin server.
- Enable full-page caching. For WordPress: WP Rocket, LiteSpeed Cache, or W3 Total Cache. For custom sites: Varnish, Redis, or your hosting provider's built-in caching.
- Upgrade your hosting. Shared hosting is the single biggest TTFB killer for WordPress sites. Moving to managed hosting (Kinsta, WP Engine, Cloudways) often cuts TTFB from 1,500ms to under 200ms overnight.
Fix 2: Preload your LCP image
This is the highest-impact, lowest-effort LCP fix available. Add this in the <head> of your page, before any stylesheets:
Also add the fetchpriority attribute directly on the <img> tag:
Critical mistake to avoid: Never add loading="lazy" to your LCP image. Lazy loading tells the browser to delay loading the image — the exact opposite of what you want for your most important element.
Fix 3: Compress and convert your images
Even with preloading, a 2MB hero image will never hit a 2.5s LCP target on mobile.
- Convert to WebP or AVIF. WebP is typically 30–50% smaller than JPEG at the same visual quality. Use WebP as your default.
- Compress aggressively. For most hero images, 80% quality in WebP is indistinguishable from 100%. Use Squoosh, ImageOptim, or an automated pipeline like Cloudinary.
- Use srcset for responsive images. There is no reason to send a 2,400px image to a phone.
- Set explicit width and height attributes. This also prevents CLS (covered below).
Fix 4: Eliminate render-blocking resources
Render-blocking resources prevent the browser from painting anything until they finish loading.
- Inline critical CSS. CSS needed to render above-the-fold content should go directly in a
<style>tag in the<head>. - Defer non-critical JavaScript. Add the
deferattribute to any<script>tag not needed for the initial render. - Async third-party scripts. Add
asyncto analytics, chat widgets, and marketing pixels. Never load these synchronously.
LCP quick wins: Preload the hero image with
fetchpriority="high"→ Removeloading="lazy"from the LCP element → Convert images to WebP → Add a CDN → Enable server-side caching
How to fix INP (Interaction to Next Paint)
INP replaced FID as an official Core Web Vital in March 2024. The target is 200ms or less.
INP measures the worst interaction across an entire page session — not just the first click. This makes it significantly harder to pass than FID, especially for JavaScript-heavy sites.
What causes poor INP?
Almost all INP problems have the same root cause: the browser's main thread is busy doing JavaScript work when the user tries to interact.
- Long JavaScript tasks. Any task running for more than 50ms on the main thread is a "long task." It directly delays the browser's response to user input.
- Heavy third-party scripts. Analytics, chat widgets, advertising tags, and A/B testing tools all run on the main thread. If they fire when a user clicks something, the click response is delayed.
- Expensive event handlers. Event listeners that trigger layout recalculations can block the main thread for hundreds of milliseconds.
- Large DOM size. Pages with more than 1,400 DOM nodes take significantly longer to recalculate styles after an interaction.
Fix 1: Break up long tasks
Open Chrome DevTools → Performance panel → record a session where you click around the page. Look for red-topped bars in the Main thread timeline — these are long tasks.
To break them up, yield back to the browser between chunks of work:
// Instead of one blocking function:
async function processLargeList(items) {
for (const item of items) {
heavyWork(item);
await scheduler.yield(); // gives the browser a chance to respond
}
}Use setTimeout(fn, 0) as a fallback for browsers that do not support scheduler.yield() yet.
Fix 2: Reduce and defer third-party scripts
Third-party scripts are often the biggest INP killer — and they are entirely outside your codebase.
- Open PageSpeed Insights and look for "Reduce the impact of third-party code"
- Audit the list honestly — remove scripts from tools you no longer actively use
- For scripts you keep: load them after the page is interactive using
deferor by injecting them after theloadevent - For scripts that must run early: consider Partytown, which offloads third-party scripts to a web worker
Reality check: A typical marketing site loads 10–20 third-party scripts. Each one competes for main thread time. If your INP is failing, at least one third-party script is almost certainly responsible.
Fix 3: Optimise your event handlers
- Debounce scroll and resize handlers. These fire hundreds of times per second without debouncing.
- Avoid layout thrashing. Never read
offsetWidth,offsetHeight, orgetBoundingClientRectinside a loop or immediately after writing to the DOM. - Use CSS for animations, not JavaScript. CSS animations run on the compositor thread and do not block user interactions.
- Reduce React re-renders. Use
React.memo,useMemo, anduseCallbackto prevent unnecessary re-renders on interaction.
INP quick wins: Find long tasks in DevTools Performance panel → Audit and remove unused third-party scripts → Defer remaining third-party scripts until after page load → Add
scheduler.yield()to long synchronous functions
How to fix CLS (Cumulative Layout Shift)
CLS measures how much your page content unexpectedly shifts while loading. A score above 0.1 means users are seeing visible instability — buttons moving, text jumping, links shifting just as they are about to click.
The target is 0.1 or less. CLS is the most fixable of the three vitals once you know what is causing it.
What causes layout shift?
- Images without explicit dimensions. The browser allocates no space for an image until it loads. When it does load, it pushes everything below it down.
- Web fonts causing FOUT/FOIT. When a custom font swaps in, text reflows if the fallback font has different dimensions.
- Dynamically injected content. Cookie banners, promo bars, chat widgets, and ad slots inserted above existing content after page load push everything down.
- Animating layout properties. Animating
top,left,width, orheighttriggers layout recalculation. Only animatetransformandopacity.
Fix 1: Add explicit dimensions to all images and videos
This is the single highest-impact CLS fix for most websites:
html
<img src="hero.webp" width="1200" height="600" alt="Hero image" fetchpriority="high">For responsive images, combine explicit dimensions with CSS:
css
img {
width: 100%;
height: auto; /* browser uses width/height to calculate aspect ratio */
}If you do not know the exact dimensions (user-uploaded images), use aspect-ratio:
css
.image-container {
aspect-ratio: 16 / 9;
width: 100%;
overflow: hidden;
}Fix 2: Fix font-induced layout shift
- Use
font-display: optional. The browser uses the fallback font if the custom font is not cached. Zero layout shift — users on their first visit see the fallback font. - Preload key fonts. Add
<link rel="preload" as="font" type="font/woff2" crossorigin>in<head>for any font used above the fold. - Use
size-adjust. If you usefont-display: swap, pair it withsize-adjust,ascent-override, anddescent-overrideto match the fallback font's metrics to your custom font.
Fix 3: Reserve space for dynamic content
- Give ad slots a fixed minimum height. Even before the ad loads, give the container a
min-heightmatching the ad unit size. - Use
position: fixedfor cookie banners. A fixed banner overlays content instead of pushing it down. - Never inject promo bars via JavaScript after page load. Include them in the initial HTML so the browser accounts for their height from the start.
CLS quick wins: Add
widthandheightto everyimgandvideo→ Addaspect-ratioCSS to responsive containers → Preload above-the-fold fonts → Give ad slots amin-height→ Animate onlytransformandopacity
Platform-specific fixes
WordPress
WordPress is where Core Web Vitals problems are most common — primarily because of plugin bloat and unoptimised themes.
- Install a caching plugin. WP Rocket is the most complete all-in-one solution (paid). LiteSpeed Cache is free and excellent if your host runs LiteSpeed.
- Install an image optimisation plugin. Imagify, ShortPixel, or Smush automatically convert images to WebP and compress them on upload.
- Switch to a lightweight theme. GeneratePress, Kadence, and Blocksy load under 50KB. Most popular themes (Divi, Avada) ship with hundreds of kilobytes you do not need.
- Audit and remove unused plugins. Every active plugin adds JavaScript and CSS to your pages, even pages where it is not needed.
- Add a CDN. Cloudflare's free plan cuts TTFB by 50% or more for international users.
Shopify
Shopify manages hosting for you, so TTFB is rarely the issue. The problems come from themes and apps.
- Choose a lightweight theme. Dawn (Shopify's free default) is well-optimised. Many third-party themes ship with jQuery and multiple CSS frameworks that hurt performance.
- Audit your apps aggressively. Each installed Shopify app typically adds 50–200ms of JavaScript execution. Remove any app you are not actively using.
- Never lazy-load your main product image. It is almost always the LCP element. Keep it
loading="eager".
Next.js and React
React apps often have good lab scores but poor INP in the field. React's rendering model creates long tasks on interaction.
- Use
next/imagefor all images. It automatically handles WebP, lazy loading, explicit dimensions, and CDN delivery. - Use dynamic imports.
next/dynamiccode-splits heavy components so they only load when needed. - Profile React re-renders. Use React DevTools Profiler to find components that re-render unnecessarily on interaction.
- Measure INP in production. Instrument the
web-vitalslibrary in your app and send real INP data to your analytics platform.
How to monitor Core Web Vitals ongoing
Fixing your CWV once is not enough. Every new plugin, JavaScript library, or image upload is a potential regression.
- Google Search Console — check weekly. The Core Web Vitals report shows field data for all your URLs and how scores change over time.
- PageSpeed Insights — run after every deployment. Test your key landing pages after any significant code or content change.
- web-vitals JavaScript library — real-user monitoring. Captures real CWV data from actual visitors and sends it to Google Analytics or any analytics platform.
- Lighthouse CI — in your deployment pipeline. Runs Lighthouse on every pull request and blocks merges that cause performance regressions.
When to re-test: After any theme change → After adding/removing plugins or apps → After a hosting migration → After adding new third-party scripts → Quarterly as a standard SEO health check.
Timeline: Google's CrUX data updates monthly. After deploying fixes, expect Search Console to reflect improvements within 28–35 days.
Frequently asked questions
Q1. Do Core Web Vitals directly affect my Google rankings?
Yes, but they are a tiebreaker — not the primary ranking factor. For competitive keywords where content quality is similar between pages, CWV can be the deciding factor.
Q2. How long until I see improvements in Search Console?
Google's CrUX data is collected over a rolling 28-day window. Expect improvements to show up in Search Console within 28–35 days of deploying your fixes.
Q3. My Lighthouse score is 90+, but Search Console still shows poor CWV. Why?
Lighthouse uses simulated lab data on a high-end device with a fast connection. Search Console uses real-user data, including older phones and slow mobile connections. The two regularly disagree. Field data is what Google uses — always prioritise fixing that over chasing a lab score.
Q4. Can I pass on desktop and fail on mobile?
Yes. Google measures desktop and mobile completely separately. Both need to pass to get the full Page Experience benefit. Mobile is almost always harder to fix.
Q5. Is INP harder to fix than FID was?
Significantly. FID only measured the first interaction on page load. INP measures every interaction across the entire session and uses the worst one. JavaScript-heavy sites — React apps, ecommerce pages with filters, sites with heavy chat widgets — will find INP the hardest to pass.
Summary and next steps
Most sites can move from Poor to Good on all three metrics in 4–8 weeks of focused work. Here is the approach:
- Diagnose first. Open Search Console and PageSpeed Insights. Find which metric is failing on which pages.
- Fix LCP first. For most sites, this is the biggest issue. A TTFB fix and hero image preload alone often move LCP from Poor to Good.
- Then fix CLS. Usually, image dimensions and font loading fixes.
- Then fix INP. JavaScript auditing and third-party script reduction.
- Monitor monthly. Check Search Console weekly and run PageSpeed Insights after every major deployment.
Every day your Core Web Vitals are failing is a day a competitor with a faster page has the edge. The fixes are well-documented, the tools are free, and the improvements compound over time.
