Core Web Vitals Optimization Guide for 2026
A comprehensive playbook for mastering LCP, INP, and CLS scores to boost user experience and search engine rankings.
Listen to Article
PlayingClick play to listen to audio narration

Table of Contents
Googleβs Core Web Vitals remain one of the most critical factors for search ranking and user retention. Here is how to achieve 100/100 Lighthouse scores.
1. Optimizing LCP (Largest Contentful Paint)
LCP measures how fast the main visual element loads. Target score: Under 2.5 seconds.
Strategies:
- Preload Hero Images: Always preload your LCP hero image in
<head>. - Use Next-Gen Formats: Serve images in AVIF or WebP formats.
- Minimize Render-Blocking Resources: Inline critical CSS and defer non-essential scripts.
<link rel="preload" as="image" href="/hero-image.avif" type="image/avif" />
2. Mastering INP (Interaction to Next Paint)
INP measures overall responsiveness to user inputs. Target score: Under 200 milliseconds.
Strategies:
- Break Up Long Tasks: Use
requestIdleCallbackor yield to main thread. - Reduce JavaScript Execution: Avoid heavy main-thread computations during user interactions.
- Optimize Event Listeners: Debounce scroll and resize listeners.
3. Eliminating CLS (Cumulative Layout Shift)
CLS measures visual stability. Target score: Under 0.1.
Strategies:
- Explicitly set
widthandheightattributes on images and video containers. - Reserve space for dynamic web ads and banners.
- Avoid inserting dynamic content above existing elements without user trigger.
The Three Core Web Vitals
Google uses three metrics as its primary user-experience signals for ranking:
| Metric | Measures | Good Threshold |
|---|---|---|
| LCP (Largest Contentful Paint) | Loading performance | β€ 2.5 s |
| INP (Interaction to Next Paint) | Responsiveness | β€ 200 ms |
| CLS (Cumulative Layout Shift) | Visual stability | β€ 0.1 |
INP replaced FID (First Input Delay) as an official Core Web Vital in March 2024.
Improving LCP
LCP measures when the largest visible element (hero image, heading) is painted.
1. Preload your hero image
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" />
2. Use modern image formats
WebP provides 25β35% smaller files than JPEG at equivalent quality. AVIF provides 50% smaller files.
<picture>
<source srcset="/hero.avif" type="image/avif" />
<source srcset="/hero.webp" type="image/webp" />
<img src="/hero.jpg" alt="Hero" width="1200" height="630" loading="eager" fetchpriority="high" />
</picture>
3. Eliminate render-blocking resources
Move non-critical CSS to async loads. Inline critical CSS for above-the-fold content.
<!-- Async non-critical CSS -->
<link rel="preload" href="/styles/below-fold.css" as="style" onload="this.rel='stylesheet'" />
4. Use a CDN
Serving assets from a CDN node geographically close to the user reduces TTFB (Time to First Byte), directly improving LCP.
Improving INP
INP measures the delay between a user interaction (click, tap, keypress) and the next visual update.
Causes of high INP:
- Long JavaScript tasks blocking the main thread
- Synchronous event handlers executing heavy computation
- Large DOM trees requiring expensive re-layout
Fix: Break long tasks with scheduler.yield() or setTimeout(0)
async function handleButtonClick() {
processFirstBatch();
await scheduler.yield(); // Yield to browser, allowing paint
processSecondBatch();
}
Fix: Debounce input handlers
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
const onSearch = debounce(fetchResults, 200);
input.addEventListener('input', onSearch);
Improving CLS
CLS measures unexpected layout shifts during the page lifecycle.
Most common causes:
- Images without explicit width and height attributes
- Ads or embeds inserted without reserved space
- Web fonts causing a flash of invisible/unstyled text (FOIT/FOUT)
Fix: Always declare image dimensions
<!-- Always include width and height so the browser reserves space -->
<img src="/photo.webp" width="800" height="450" alt="..." />
Fix: Reserve space for dynamic content
.ad-slot {
min-height: 250px; /* Reserve expected height before ad loads */
}
Fix: Use font-display: optional
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: optional; /* Use fallback if font not ready at render */
}
Measuring Core Web Vitals
Field data (real user measurements):
- Google Search Console β Core Web Vitals report
- Chrome User Experience Report (CrUX)
- Web Vitals JavaScript library
Lab data (controlled measurement):
- Lighthouse in Chrome DevTools (Ctrl+Shift+I β Lighthouse tab)
- PageSpeed Insights at pagespeed.web.dev
# Run Lighthouse from the CLI
npx lighthouse https://example.com --output html --view
Field data is what Google uses for ranking. Lab data is useful for debugging.
Written by Editorial Team
Tech contributor covering software architecture, AI research, cloud infrastructure, and systems engineering practices.