Core Web Vitals Optimization Guide 2025 | LCP, INP & CLS
Complete Core Web Vitals optimization guide for 2025. Learn how to improve LCP, INP, and CLS to pass Google's thresholds, boost rankings, and improve UX.
Core Web Vitals are Google’s official user experience metrics and a confirmed ranking factor since 2021. In 2025, these metrics are more important than ever, with Google using them to determine page experience scores that directly impact search rankings.
If your site has poor Core Web Vitals, you’re losing rankings to competitors—even if your content is better. This guide shows you exactly how to optimize each Core Web Vital metric to pass Google’s thresholds and improve both SEO and user experience.
Quick Wins (Start Here)
- Test your site in seconds with the free Core Web Vitals checker
- Fix your biggest loading issue first: LCP optimization guide
- Fix sluggish interactivity: INP optimization guide
- If you need automated tracking: Core Web Vitals monitoring
What Are Core Web Vitals?
Core Web Vitals are three specific metrics that measure real-world user experience:
- LCP (Largest Contentful Paint) - Loading performance
- INP (Interaction to Next Paint) - Interactivity (replaced FID in 2024)
- CLS (Cumulative Layout Shift) - Visual stability
Together, these metrics capture the most frustrating aspects of poor web performance: slow loading, unresponsive interactions, and unexpected layout shifts.
Why Core Web Vitals Matter for SEO
Google’s official statement: “Page experience signals in ranking will include Core Web Vitals in addition to our existing signals for mobile-friendliness, safe-browsing, HTTPS-security, and intrusive interstitial guidelines.”
What this means:
- Core Web Vitals are a direct ranking factor
- Sites with better CWV scores can outrank sites with worse scores (all else equal)
- Mobile and desktop are scored separately
- Poor CWV scores can prevent your site from appearing in Google’s top stories
Real-world impact:
- Study by Portent: 1-second delay in page load time = 7% drop in conversions
- Google data: 53% of mobile users abandon sites that take longer than 3 seconds to load
- Better CWV scores = better rankings = more traffic = more revenue
Core Web Vitals Thresholds (2025)
Google defines three scoring zones for each metric:
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP | ≤ 2.5s | 2.5s - 4.0s | > 4.0s |
| INP | ≤ 200ms | 200ms - 500ms | > 500ms |
| CLS | ≤ 0.1 | 0.1 - 0.25 | > 0.25 |
Goal: Get 75% of your page views in the “Good” range for all three metrics.
Where to check your scores:
- Google PageSpeed Insights
- Google Search Console (Core Web Vitals report)
- Chrome DevTools (Lighthouse)
- Rankture Free SEO Audit
1. Largest Contentful Paint (LCP) Optimization
What LCP Measures: The render time of the largest image or text block visible in the viewport.
Why it matters: LCP represents perceived loading performance—when does the user see the main content?
Target: ≤ 2.5 seconds
Common LCP Elements
Your LCP element is usually:
- Hero image
- Header image in blog posts
- Product image on e-commerce pages
- Large text block (if no images above fold)
How to identify your LCP element:
- Open Chrome DevTools → Performance tab
- Record page load
- Look for “LCP” marker in timeline
- Inspect which element triggered it
How to Optimize LCP
1. Optimize Images
Problem: Large, unoptimized images are the #1 cause of slow LCP.
Solutions:
a) Use modern image formats:
- WebP (30% smaller than JPEG, supported in all modern browsers)
- AVIF (50% smaller than JPEG, growing support)
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero image" width="1200" height="600">
</picture>
b) Compress images:
- Use tools like TinyPNG, ImageOptim, or Squoosh
- Aim for 100-200KB for hero images
- Use lossy compression (70-85% quality)
c) Resize images:
- Don’t serve 4000px images when displaying at 1200px
- Create multiple sizes for responsive design
d) Set explicit dimensions:
<img src="hero.jpg" alt="Hero" width="1200" height="600" loading="eager">
e) Use loading=“eager” for above-fold images:
<img src="hero.jpg" alt="Hero" loading="eager">
Don’t use loading="lazy" on LCP images—it delays loading!
2. Preload Critical Resources
Tell the browser to fetch important resources immediately:
<link rel="preload" as="image" href="/hero.webp" type="image/webp">
<link rel="preload" as="font" href="/fonts/inter.woff2" type="font/woff2" crossorigin>
What to preload:
- LCP image
- Critical web fonts
- Critical CSS
Don’t over-preload: Only preload 2-3 truly critical resources.
3. Reduce Server Response Time (TTFB)
TTFB (Time to First Byte) = how long until the server starts sending data
Target: < 600ms
How to improve TTFB:
- Upgrade hosting: Shared hosting = slow. Use VPS or cloud hosting (AWS, Google Cloud, DigitalOcean)
- Use a CDN: Cloudflare, Fastly, or CloudFront serve content from locations near users
- Enable server-side caching: Cache HTML for logged-out users
- Optimize database queries: Slow queries = slow TTFB
- Use HTTP/2 or HTTP/3: Faster protocol = faster delivery
4. Eliminate Render-Blocking Resources
Render-blocking resources = CSS/JS files that prevent the page from displaying until they’re loaded.
How to fix:
a) Inline critical CSS:
<style>
/* Inline only above-fold CSS here */
.hero { background: blue; padding: 100px; }
</style>
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
b) Defer non-critical JavaScript:
<script src="/analytics.js" defer></script>
c) Use async for independent scripts:
<script src="/chat-widget.js" async></script>
Difference:
defer= load in background, execute after DOM ready (maintains order)async= load in background, execute immediately when loaded (doesn’t maintain order)
5. Use Server-Side Rendering (SSR)
For React/Vue/Angular apps, use SSR or static site generation to serve pre-rendered HTML.
Frameworks with built-in SSR:
- Next.js (React)
- Nuxt (Vue)
- Astro (multi-framework)
- SvelteKit
Why it helps: User sees content immediately, before JavaScript loads.
2. Interaction to Next Paint (INP) Optimization
What INP Measures: The time from user interaction (click, tap, keyboard) to the next visual update.
Why it matters: INP captures overall responsiveness—does the site feel snappy or sluggish?
Target: ≤ 200ms
Note: INP replaced FID (First Input Delay) in 2024. FID only measured the delay before processing starts; INP measures the full interaction latency.
Common INP Issues
- Heavy JavaScript execution blocking the main thread
- Long-running event handlers
- Excessive DOM manipulation
- Third-party scripts (ads, chat widgets)
How to Optimize INP
1. Break Up Long Tasks
Problem: JavaScript tasks > 50ms block the main thread and delay interactions.
Solution: Break long tasks into smaller chunks using setTimeout or requestIdleCallback:
// Bad: Long blocking task
function processLargeArray(items) {
for (let i = 0; i < items.length; i++) {
// Complex processing
}
}
// Good: Chunked processing
async function processLargeArray(items, chunkSize = 100) {
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
chunk.forEach(item => {
// Process item
});
// Yield to browser
await new Promise(resolve => setTimeout(resolve, 0));
}
}
2. Optimize Event Handlers
Problem: Heavy event handlers (click, scroll, input) delay interactions.
Solutions:
a) Debounce/throttle expensive operations:
// Debounce search input
let timeout;
searchInput.addEventListener('input', (e) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
performSearch(e.target.value);
}, 300);
});
b) Use passive event listeners:
// Tells browser it's safe to scroll while handler runs
element.addEventListener('touchstart', handler, { passive: true });
c) Avoid layout thrashing:
// Bad: Read-write-read-write (causes multiple reflows)
elements.forEach(el => {
const height = el.offsetHeight; // Read
el.style.height = height + 10 + 'px'; // Write
});
// Good: Read all, then write all
const heights = elements.map(el => el.offsetHeight); // Read batch
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px'; // Write batch
});
3. Code Splitting
Problem: Loading massive JavaScript bundles delays everything.
Solution: Split code so users only load what they need:
Webpack example:
// Instead of importing everything
import { moduleA, moduleB, moduleC } from './utils';
// Dynamically import when needed
button.addEventListener('click', async () => {
const { moduleA } = await import('./utils');
moduleA.doSomething();
});
Next.js example:
import dynamic from 'next/dynamic';
const DynamicComponent = dynamic(() => import('../components/Heavy'), {
loading: () => <p>Loading...</p>,
});
4. Web Workers for Heavy Computation
Move CPU-intensive work off the main thread:
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
console.log('Result:', e.data);
};
// worker.js
self.onmessage = (e) => {
const result = processData(e.data);
self.postMessage(result);
};
Use web workers for:
- Image processing
- Data parsing (large JSON)
- Calculations
- Encryption
5. Minimize Third-Party Impact
Problem: Third-party scripts (ads, analytics, chat widgets) can destroy INP.
Solutions:
a) Load third-parties last:
<script src="/your-app.js"></script>
<!-- Load third-parties after your code -->
<script src="https://cdn.thirdparty.com/widget.js" defer></script>
b) Use facade patterns: Instead of loading YouTube/Vimeo immediately, show a poster image and only load the iframe when clicked.
c) Self-host analytics: Instead of Google Analytics, use lightweight self-hosted solutions like Plausible or Fathom.
3. Cumulative Layout Shift (CLS) Optimization
What CLS Measures: Unexpected layout shifts—how much visible content moves around during page load.
Why it matters: Nothing is more frustrating than clicking a button, only for an ad to load and make you click the wrong thing.
Target: ≤ 0.1
Common CLS Causes
- Images without dimensions
- Ads, embeds, iframes without reserved space
- Web fonts causing text reflow (FOIT/FOUT)
- Dynamically injected content
- Animations that trigger layout
How to Optimize CLS
1. Set Explicit Dimensions on Images/Videos
Bad (causes CLS):
<img src="product.jpg" alt="Product">
Good (reserves space):
<img src="product.jpg" alt="Product" width="800" height="600">
Responsive images:
<img src="product.jpg" alt="Product"
width="800" height="600"
style="width: 100%; height: auto;">
Modern browsers use width and height to calculate aspect ratio, even with responsive CSS.
2. Reserve Space for Ads and Embeds
Bad:
<div id="ad-slot"></div>
<script>loadAd('#ad-slot')</script>
Good:
<div id="ad-slot" style="min-height: 250px;">
<!-- Ad loads here -->
</div>
Better (aspect ratio box):
<div style="aspect-ratio: 16/9; background: #f0f0f0;">
<iframe src="youtube-video"></iframe>
</div>
3. Optimize Web Font Loading
Problem: Web fonts cause FOUT (Flash of Unstyled Text) or FOIT (Flash of Invisible Text), both causing CLS.
Solutions:
a) Use font-display: swap:
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap; /* Show fallback immediately */
}
b) Preload critical fonts:
<link rel="preload" as="font" href="/fonts/inter.woff2"
type="font/woff2" crossorigin>
c) Match fallback font metrics: Use a fallback font with similar dimensions to reduce CLS when fonts swap.
d) Consider system fonts:
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
Roboto, "Helvetica Neue", Arial, sans-serif;
Zero CLS, zero load time.
4. Avoid Inserting Content Above Existing Content
Bad:
// Loads content, pushes everything down
banner.insertAdjacentHTML('afterend', newContent);
Good:
// Reserve space first
const placeholder = document.createElement('div');
placeholder.style.minHeight = '200px';
banner.after(placeholder);
// Load content into reserved space
fetchContent().then(content => {
placeholder.innerHTML = content;
});
5. Use CSS Transforms for Animations
Bad (causes layout shift):
.box:hover {
width: 200px; /* Triggers layout */
}
Good (no layout shift):
.box:hover {
transform: scale(1.1); /* GPU-accelerated */
}
GPU-accelerated properties (no layout shift):
transformopacityfilter
Layout-triggering properties (causes CLS):
width,heightmargin,paddingtop,left,right,bottom(with non-transform positioning)
Tools to Measure Core Web Vitals
1. Google PageSpeed Insights
Pros:
- Free, official Google tool
- Shows both lab data (Lighthouse) and field data (real users)
- Specific recommendations
Cons:
- Only tests one URL at a time
- Can vary between runs
Because it tests one URL at a time, PageSpeed Insights is poor at spotting template-level problems. The Core Web Vitals checker runs across your pages at once and groups failures by metric, so a bad template shows up as a cluster rather than a URL you happened to pick.
2. Google Search Console
Dashboard → Experience → Core Web Vitals
Pros:
- Real user data from Chrome browsers
- Grouped by mobile/desktop
- Shows all URLs with issues
Cons:
- 28-day lag (historical data)
- Requires enough traffic to generate data
If this report is where you found out your Core Web Vitals assessment failed, start there — that 28-day window changes how you should read the verdict, and it explains most cases of passing Lighthouse while failing in Search Console.
3. Chrome DevTools (Lighthouse)
Chrome → DevTools → Lighthouse tab
Pros:
- Built into Chrome
- Detailed performance timeline
- Can simulate slow networks/devices
Cons:
- Lab data only (not real users)
- Requires technical knowledge
4. Rankture SEO Audit
Pros:
- Automated Core Web Vitals checks
- AI-powered fix suggestions
- Tracks improvements over time
- Non-technical explanations
Cons:
- Requires signup for advanced features
5. WebPageTest
Pros:
- Test from multiple locations/devices
- Filmstrip view of page load
- Extremely detailed waterfall charts
Cons:
- Complex interface
- Overwhelming for beginners
Core Web Vitals Optimization Checklist
LCP Checklist
- Compress and resize images (WebP/AVIF format)
- Set
widthandheighton LCP image - Use
loading="eager"on LCP image - Preload LCP image:
<link rel="preload" as="image"> - Reduce server response time (< 600ms TTFB)
- Use a CDN for images and assets
- Eliminate render-blocking CSS/JS
- Inline critical CSS
- Defer non-critical JavaScript
- Enable HTTP/2 or HTTP/3
- Use server-side caching
INP Checklist
- Break up long JavaScript tasks (< 50ms)
- Optimize event handlers (debounce, passive listeners)
- Code split large bundles
- Use web workers for heavy computation
- Minimize third-party script impact
- Avoid layout thrashing in JavaScript
- Defer non-critical scripts
- Monitor Total Blocking Time (TBT)
- Profile JavaScript performance in DevTools
- Remove unused JavaScript
CLS Checklist
- Set dimensions on all images/videos
- Reserve space for ads and embeds
- Use
font-display: swapon web fonts - Preload critical fonts
- Match fallback font metrics
- Avoid inserting content above existing content
- Use CSS transforms for animations (not width/height)
- Set
min-heighton dynamic content containers - Avoid auto-expanding content
- Test across different viewport sizes
Common Core Web Vitals Issues & Fixes
Issue: “LCP element was a background image”
Problem: CSS background images can’t be preloaded easily.
Fix: Use <img> tags instead of CSS backgrounds for critical images:
<!-- Instead of: -->
<div class="hero" style="background-image: url('/hero.jpg')"></div>
<!-- Use: -->
<div class="hero">
<img src="/hero.jpg" alt="Hero" loading="eager">
</div>
Issue: “INP poor due to third-party scripts”
Problem: Ads, analytics, and chat widgets slow interactions.
Fix: Load third-parties after user interaction:
let analyticsLoaded = false;
document.addEventListener('scroll', () => {
if (!analyticsLoaded) {
const script = document.createElement('script');
script.src = 'https://analytics.com/tracker.js';
document.body.appendChild(script);
analyticsLoaded = true;
}
}, { once: true });
Issue: “CLS from web fonts”
Problem: Text shifts when web fonts load.
Fix: Use font-display: optional for non-critical fonts:
@font-face {
font-family: 'DecorativeFont';
src: url('/fonts/decorative.woff2') format('woff2');
font-display: optional; /* Only use if available immediately */
}
Issue: “Mobile CWV good, desktop poor” (or vice versa)
Problem: Different content/scripts for mobile vs desktop.
Fix: Use responsive design with same HTML for both. Optimize images with srcset:
<img srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px"
src="large.jpg" alt="Responsive image">
Conclusion
Core Web Vitals are now a mandatory part of SEO. Sites with excellent CWV scores have a ranking advantage over slower competitors, especially in competitive niches.
Focus areas by priority:
- LCP - Biggest impact on rankings, most noticeable to users
- CLS - Second biggest impact, most frustrating to users
- INP - Important but harder to measure/optimize
Quick wins:
- Compress images (30 minutes, huge LCP improvement)
- Add width/height to images (10 minutes, fixes CLS)
- Defer third-party scripts (15 minutes, improves INP)
Long-term optimizations:
- Upgrade hosting/implement CDN
- Migrate to modern framework with SSR
- Hire performance consultant for complex issues
Get a Free Core Web Vitals Audit
Want to know your exact CWV scores and get AI-powered recommendations?
Run a free SEO audit with Rankture and get:
- ✅ Real Core Web Vitals scores (LCP, INP, CLS)
- ✅ Specific issues causing poor scores
- ✅ AI-generated fixes you can copy-paste
- ✅ Before/after tracking over time
No signup required for your first audit. Get results in 60 seconds.
Related Articles
Tags:
Share this article:
Ready to improve your SEO?
Get a free SEO audit and see exactly what needs fixing on your site
Start Free Audit