Improve LCP, INP, and CLS — step-by-step fixes for slow websites (includes tools for minification and image optimization)
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP Largest Contentful Paint | ≤ 2.5s | 2.5–4s | > 4s |
| INP Interaction to Next Paint (replaced FID March 2024) | ≤ 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 (usually image, video, or large text block) becomes visible. Poor LCP means slow loading hero image, render-blocking CSS/JS, or slow server response.
TTFB (Time to First Byte) > 600ms hurts LCP. Often caused by slow backend, unoptimized database queries, or no caching.
webpagetest.org to break down TTFB. Check server logs for slow endpoints.Browser can't paint LCP element until blocking CSS/JS loads and executes. Critical CSS is inline or deferred; non-critical CSS loads async.
<head>.<style>
/* critical CSS: navbar, hero bg */
</style><link rel="preload" as="style" href="non-critical.css"
onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="non-critical.css"></noscript>defer or async attribute.<script src="analytics.js" defer></script>LCP element is often a large image or video. If not optimized, it takes too long to download and render.
<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><link rel="preload" as="image" href="hero.webp" imagesrcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w" imagesizes="100vw">loading="eager" for LCP image (default for above-the-fold), loading="lazy" for below-fold.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 to avoid CORS preflight.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.
JavaScript running > 50ms delays paint after interaction. Break up heavy work or move off-main-thread.
setTimeout or requestIdleCallback:function processChunk(items) {
const chunk = items.splice(0, 50); // 50 items per chunk
chunk.forEach(processItem);
if (items.length) {
setTimeout(() => processChunk(items), 0);
}
}// main.js
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.onmessage = (e) => { /* result */ };// worker.js (runs off main thread)
self.onmessage = (e) => {
const result = heavyCalc(e.data);
self.postMessage(result);
};async/await with setTimeout or requestAnimationFrame to yield to the browser. Chrome DevTools → Performance panel identifies longest tasks.Large JS downloads, parses, and executes slowly, delaying interaction handlers. Code-split, tree-shake, and minify.
// Dynamic import (React.lazy, Vue async components, etc.)
import('./heavy-module.js').then(module => {
module.init();
});defer/async or after DOMContentLoaded.requestIdleCallback to 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.
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 (modern) -->
<img src="photo.jpg" style="aspect-ratio: 4/3; width: 100%; height: auto;">aspect-ratio. Legacy: padding-bottom hack.Banners, notifications, or ads that appear after page load push content down. Reserve space in advance.
<div id="banner-container" style="min-height: 80px;"></div>aspect-ratio to reserve ad slot dimensions before network response.Web fonts load after system font fallback. Switch causes text size/width to shift (FOUT — flash of unstyled text) or invisible (FOIT — flash of invisible text).
font-display: swap in @font-face (default in Google Fonts since 2020):@font-face {
font-family: 'MyFont';
src: url('myfont.woff2') format('woff2');
font-display: swap;
}<link rel="preload" as="font" href="/fonts/inter.woff2" type="font/woff2" crossorigin>font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;. Zero CLS from font loading. Accept tradeoff of brand consistency.The Core Web Vitals fix guide provides actionable solutions for LCP, INP, and CLS optimization. Each section explains the problem, offers step-by-step code fixes, and links to relevant OmniTools (CSS/JS Minifiers, Image Resizer). Improve your PageSpeed score and user experience with these proven techniques.
Optimize hero image (resize, WebP/AVIF, preload), inline critical CSS, defer non-critical JS, enable CDN, reduce TTFB with caching.
Break long JS tasks (>50ms) with setTimeout/worker. Debounce events. Minify and code-split. Avoid synchronous layout (layout thrashing).
Set explicit width/height on images, use aspect-ratio CSS, reserve space for ads/banners, font-display: swap, avoid injecting content above fold after load.
CSS Minifier, JS Minifier, Image Resizer, GIF Optimizer. All free, client-side, zero upload.
Google replaced First Input Delay (FID) with Interaction to Next Paint (INP) in March 2024 because INP captures the full interaction latency — not just the delay until first input is processed, but until the page visually responds. FID only measured the first interaction; INP measures all interactions (click, tap, keypress) and reports worst (or p75). More representative of real user experience.
Use Chrome DevTools: open Lighthouse panel, select "Performance" category, check "Core Web Vitals" metrics. Or use the PageSpeed Insights API. For field data, use the Chrome User Experience Report (CrUX) or Web Vitals Chrome extension. Testing locally on localhost gives lab data; real-user data requires public URL.
Yes — smaller JS/CSS means faster download, parse, and compile. For a site with 500KB of JS, minification might reduce to 300KB (40% smaller). Combined with gzip/brotli compression on server, transfer size drops further. Each 100KB reduction can shave ~0.5–1s off LCP on 3G. But focus on LCP blockers first: images and render-blocking resources.
Yes, completely free with no sign-up required.