Fix Core Web Vitals — LCP, INP and CLS
Improve LCP, INP, and CLS — step-by-step fixes for slow websites (includes tools for minification and image optimization)
Core Web Vitals (2024→2025 thresholds)
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP Largest Contentful Paint | = 2.5s | 2.5→4s | > 4s |
| INP Interaction to Next Paint | = 200ms | 200→500ms | > 500ms |
| CLS Cumulative Layout Shift | = 0.1 | 0.1→0.25 | > 0.25 |
Test your site: PageSpeed Insights — web.dev/measure — Chrome DevTools ? Lighthouse panel
Measures when the largest content element becomes visible. Poor LCP means slow loading hero image, render-blocking CSS/JS, or slow server response.
LCP Server response time (TTFB) is slow
TTFB (Time to First Byte) > 600ms hurts LCP. Often caused by slow backend, unoptimized database queries, or no caching.
- 1Enable server-side caching (Redis, Varnish, CDN edge cache).
- 2Optimize database queries — add indexes, reduce N+1 queries.
- 3Use a CDN (Cloudflare, Fastly, Vercel edge) to serve static assets closer to users.
- 4Upgrade hosting if shared hosting is the bottleneck.
webpagetest.org to break down TTFB.LCP Render-blocking CSS or JavaScript
Browser can't paint LCP element until blocking CSS/JS loads and executes.
- 1Inline critical CSS (above-the-fold styles) directly in
<head>.
<style>/* critical CSS: navbar, hero bg */</style>- 2Load non-critical CSS asynchronously:
<link rel="preload" as="style" href="non-critical.css" onload="this.rel='stylesheet'"><noscript><link rel="stylesheet" href="non-critical.css"></noscript>- 3Defer non-critical JavaScript:
deferorasyncattribute.
<script src="analytics.js" defer></script>LCP Large, unoptimized hero image / video
LCP element is often a large image or video. If not optimized, it takes too long to download and render.
- 1Compress and resize to exact dimensions needed. Use modern formats:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" width="1200" height="600" alt="..." loading="eager">
</picture>- 2Dimensions: don't rely on CSS scaling. Ship an image close to its display size.
- 3Preload LCP image:
<link rel="preload" as="image" href="hero.webp" imagesrcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w" imagesizes="100vw">- 4Use
loading="eager"for LCP image (default for above-the-fold),loading="lazy"for below-fold.
Target widths for responsive: 400, 800, 1200, 1600, 2000px. WebP is ~30% smaller than JPEG; AVIF is even smaller.
LCP No preconnect or DNS prefetch for critical origins
Third-party resources (CDNs, fonts, APIs) add DNS lookup, TCP, TLS handshake delays. preconnect warms up connections early.
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://cdn.example.com">crossorigin needed for fonts.Measures responsiveness: time from user interaction (click, tap, keypress) to next paint. Poor INP means UI feels laggy. Often caused by long JavaScript tasks blocking the main thread.
INP Long main-thread tasks block responsiveness
JavaScript running > 50ms delays paint after interaction. Break up heavy work or move off-main-thread.
- 1Break large tasks into chunks with
setTimeoutorrequestIdleCallback:
function processChunk(items) {
const chunk = items.splice(0, 50);
chunk.forEach(processItem);
if (items.length) {
setTimeout(() => processChunk(items), 0);
}
}- 2Move expensive work to a Web Worker:
// main.js
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.onmessage = (e) => { /* result */ };// worker.js
self.onmessage = (e) => {
const result = heavyCalc(e.data);
self.postMessage(result);
};- 3Debounce or throttle frequent event handlers (scroll, resize, mousemove).
async/await with setTimeout or requestAnimationFrame to yield to the browser. Chrome DevTools ? Performance panel identifies longest tasks.INP JavaScript bundle too large
Large JS downloads, parses, and executes slowly, delaying interaction handlers. Code-split, tree-shake, and minify.
- 1Code-split: load only what's needed for current page.
import('./heavy-module.js').then(module => {
module.init();
});- 2Minify all JS — use our JS Minifier or build tool (Terser, esbuild).
- 3Lazy-load non-critical scripts with
defer/asyncor afterDOMContentLoaded.
- 4Use
requestIdleCallbackto schedule low-priority work during idle time.
setTimeout or queueMicrotask.Measures visual stability. Elements moving around unexpectedly cause poor CLS. Usually images without dimensions, dynamic content insertion, or fonts that shift text.
CLS Images without width/height attributes
Browser doesn't know image aspect ratio until it loads ? layout shifts when image renders.
<!-- Bad — no dimensions -->
<img src="photo.jpg" alt="..."><!-- Good — explicit width & height -->
<img src="photo.jpg" width="800" height="600" alt="..."><!-- For responsive: use CSS aspect-ratio -->
<img src="photo.jpg" style="aspect-ratio: 4/3; width: 100%; height: auto;">aspect-ratio. Legacy: padding-bottom hack.CLS Dynamic content inserted above existing elements
Banners, notifications, or ads that appear after page load push content down. Reserve space in advance.
- 1Reserve fixed-height container for dynamic content:
<div id="banner-container" style="min-height: 80px;"></div>- 2Or inject content off-screen (fixed/absolute positioning) then animate in without affecting flow.
- 3For ads, use
aspect-ratioto reserve ad slot dimensions before network response.
CLS Font loading causes FOIT / FOUT layout shift
Web fonts load after system font fallback. Switch causes text size/width to shift (FOUT) or invisible (FOIT).
- 1Use
font-display: swapin@font-face:
@font-face {
font-family: 'MyFont';
src: url('myfont.woff2') format('woff2');
font-display: swap;
}- 2Preload key fonts to avoid late discovery:
<link rel="preload" as="font" href="/fonts/inter.woff2" type="font/woff2" crossorigin>- 3For hero text, consider system font stack to avoid any FOIT/FOUT.
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;. Zero CLS from font loading.What Core Web Vitals are and why they matter for rankings
Core Web Vitals are three page experience metrics Google uses as a ranking signal: LCP (Largest Contentful Paint — how fast the main content loads), INP (Interaction to Next Paint — how fast the page responds to clicks and taps), and CLS (Cumulative Layout Shift — how much the page jumps around while loading). Poor scores don't tank rankings overnight, but they're a tiebreaker between pages with similar content quality.
LCP (should be under 2.5 seconds)
- Add
fetchpriority="high"to your hero image — tells the browser to load it first - Preload the LCP image —
<link rel="preload" as="image" href="hero.webp">in the<head> - Serve images in WebP or AVIF — 30—50% smaller than JPEG at the same quality
- Use a CDN — reduces TTFB which delays when LCP can even start
- Remove render-blocking CSS — inline critical CSS, defer everything else
INP (should be under 200ms)
- Break up long tasks — anything over 50ms blocking the main thread hurts INP. Use
setTimeoutorscheduler.yield()to yield between tasks - Reduce JavaScript execution time — audit with Chrome DevTools Performance panel, look for long tasks
- Avoid heavy event listeners — especially on scroll and resize
- Defer non-critical JS — use
deferorasyncon script tags
CLS (should be under 0.1)
- Set width and height on images — the browser needs dimensions to reserve space before the image loads
- Reserve space for ads and embeds — use
min-heightor aspect-ratio containers - Avoid injecting content above existing content — banners, cookie notices, and dynamic elements that push content down cause CLS
- Use
font-display: optional— prevents text from jumping when a web font loads
How to measure your Core Web Vitals
PageSpeed Insights (pagespeed.web.dev) gives you both lab data (simulated) and field data (real users from Chrome UX Report). The field data is what Google actually uses for ranking. Chrome DevTools Lighthouse gives you lab data only. Use Search Console → Core Web Vitals report for aggregate real-user data across your whole site.
Complete Developer Toolkit
Improving Core Web Vitals requires optimizing every layer of your web stack. The two highest-impact tools for LCP and INP are our CSS minifier and JS minifier — smaller CSS unblocks rendering sooner for better LCP, and smaller JS reduces main thread work for better INP. For image-heavy pages, our image tools make the biggest difference: use the image resizer to serve images at display dimensions, and the image to WebP converter to reduce image payload sizes by 25—35%.
For CLS issues caused by layout shifts from late-loading content, our SVG path visualizer helps you inline critical SVG icons directly in HTML so they render immediately without a network request. Our Base64 encoder converts small critical images to inline data URIs, eliminating render-blocking image requests. The diff checker is useful for comparing Lighthouse reports before and after optimizations to quantify improvements. When your performance issues stem from slow API responses, our API response simulator lets you test UI behavior with instant mock data. Our CORS error guide addresses the network errors that can block critical resource loading and tank your LCP score.