:::note[TL;DR]
- The three metrics: LCP (largest content loads fast ≤2.5s), INP (interactions respond fast ≤200ms), CLS (layout doesn’t shift ≤0.1)
- Fix in order: TTFB → LCP → CLS → INP — each problem is downstream of the previous
- LCP quick wins: convert hero images to WebP/AVIF, add
fetchpriority="high", set explicitwidth/height - CLS quick wins: set
width/heighton all images, reserve space for ads withmin-height - Use Search Console field data for ranking signal — Lighthouse lab scores don’t reflect real user conditions :::
Core Web Vitals are a set of performance metrics Google uses to measure user experience on the web. They directly affect your Google Search ranking. Bad scores mean lower rankings — and since 2024, Google has been increasingly aggressive about using them as a signal.
The three metrics that matter:
- LCP (Largest Contentful Paint) — how fast the main content loads
- INP (Interaction to Next Paint) — how responsive your page is to clicks and taps
- CLS (Cumulative Layout Shift) — how stable your layout is (does stuff jump around?)
What are the thresholds?
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP | ≤ 2.5s | 2.5s – 4s | > 4s |
| INP | ≤ 200ms | 200ms – 500ms | > 500ms |
| CLS | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |
“Good” means the 75th percentile of page loads hits the threshold — not just the average, the 75th percentile.
How to check your scores
Google Search Console → Core Web Vitals report — shows real-world data from Chrome users visiting your site.
PageSpeed Insights (pagespeed.web.dev) — run a URL, get lab scores plus field data if available.
Chrome DevTools → Performance tab → record a page load, look for LCP and Layout Shift markers.
Lighthouse in Chrome DevTools — Audits tab → Performance.
Field data (Search Console, PageSpeed field data) is what Google uses for ranking. Lab data (Lighthouse, PageSpeed lab score) is useful for debugging but not the ranking signal.
:::note A perfect Lighthouse score doesn’t mean good CWV field data. Lighthouse runs on your machine under ideal conditions. Real users on slow Android phones on 4G will always produce worse scores. Fix what Search Console reports — that’s the signal Google actually uses. :::
LCP — Largest Contentful Paint
LCP measures when the largest visible element (usually a hero image or H1 heading) finishes loading. It’s the metric users feel as “when did the page stop looking blank.”
What causes bad LCP
- Large unoptimized hero images (WebP/AVIF not used)
- No
priorityorpreloadon the LCP image - Render-blocking CSS or JavaScript
- Slow server response (TTFB > 600ms)
- Images loaded lazily that shouldn’t be
How to fix LCP
Preload the LCP image:
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
Use modern image formats:
<picture>
<source srcset="/hero.avif" type="image/avif">
<source srcset="/hero.webp" type="image/webp">
<img src="/hero.jpg" alt="Hero" width="1200" height="600">
</picture>
Set explicit width and height on images — prevents layout shift and helps the browser allocate space before the image loads.
Don’t lazy-load above-the-fold images:
<!-- BAD for hero image -->
<img src="hero.webp" loading="lazy">
<!-- GOOD — let it load eagerly, lazy load below-fold images -->
<img src="hero.webp" fetchpriority="high">
:::warning
Never add loading="lazy" to your LCP image (the hero or first visible large image). Lazy loading delays the fetch until the image is near the viewport — which for the LCP element means the browser waits to load the most important image on the page. Use fetchpriority="high" instead.
:::
Reduce TTFB: Use a CDN, enable HTTP/2, add aggressive server caching for static assets.
The scenario: Your LCP is 4.2 seconds and you can’t figure out why. PageSpeed says your hero image is the LCP element. It’s a 2.4 MB JPEG. You convert it to WebP (320 KB), add
fetchpriority="high", and deploy. LCP drops to 1.8 seconds. Your PageSpeed score jumps 18 points.
INP — Interaction to Next Paint
INP replaced FID in March 2024. It measures how fast your page responds to user interactions (clicks, taps, key presses) — specifically, how long until the browser paints the next frame after the interaction.
What causes bad INP
- Heavy JavaScript executing on the main thread
- Long tasks blocking the event loop (> 50ms)
- Synchronous operations on interaction handlers
- Too much third-party script
How to fix INP
Break up long tasks:
// BAD — blocks main thread for 200ms
function processItems(items) {
items.forEach(item => heavyProcess(item));
}
// GOOD — yields back to browser between chunks
async function processItems(items) {
for (const item of items) {
heavyProcess(item);
await new Promise(resolve => setTimeout(resolve, 0)); // yield
}
}
Use scheduler.yield() (modern browsers):
async function handleClick() {
doFirstThing();
await scheduler.yield(); // let browser paint, handle events
doSecondThing();
}
Defer third-party scripts. Every analytics, ad, and chat widget running on your main thread competes with your event handlers.
Profile with Chrome DevTools Performance tab — look for long tasks (red triangles) that fire after a click.
CLS — Cumulative Layout Shift
CLS measures visual stability — how much content jumps around as the page loads. You know the feeling: you’re about to tap a button, an ad loads above it, and you tap the wrong thing.
What causes bad CLS
- Images without explicit
widthandheightattributes - Ads, embeds, or iframes without reserved space
- Fonts causing FOUT (Flash of Unstyled Text) that shifts surrounding text
- Content injected above existing content after load
How to fix CLS
:::tip
Setting explicit width and height on every <img> tag is the single highest-ROI CLS fix. The browser uses these to calculate the aspect ratio and reserve space before the image loads — eliminating the shift when the image arrives. It takes seconds per image and can drop CLS from 0.3+ to near zero.
:::
Always set width and height on images:
<!-- Height reserved before image loads, no shift -->
<img src="photo.webp" width="800" height="600" alt="">
Reserve space for ads and dynamic content:
.ad-slot {
min-height: 250px; /* reserve space before ad loads */
}
Use font-display: optional or font-display: swap carefully:
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
font-display: optional; /* no FOUT — uses fallback if font isn't cached */
}
Preload critical fonts:
<link rel="preload" href="/fonts/myfont.woff2" as="font" type="font/woff2" crossorigin>
Avoid inserting content above existing content — only append below, or use CSS animations that don’t affect layout.
Priority order for fixing
If all three metrics are bad, fix in this order:
- TTFB — server response time. Everything else is downstream of this.
- LCP — most impactful for perceived load speed and ranking.
- CLS — often quick wins (add width/height to images).
- INP — requires JavaScript profiling, more work.
Tools summary
| Tool | Best for |
|---|---|
| Search Console | Real-world field data, ranking signal |
| PageSpeed Insights | Quick per-URL lab + field score |
| Chrome DevTools Lighthouse | Detailed lab audit with suggestions |
| Chrome DevTools Performance | Profiling LCP and long tasks |
| WebPageTest | Advanced analysis, waterfall charts |
| web-vitals npm package | Measuring in JavaScript, RUM setup |
Related: UX Laws Every Designer Should Know
Summary
- The three Core Web Vitals Google uses for ranking are LCP (load speed), INP (interactivity), and CLS (layout stability)
- Fix in priority order: TTFB → LCP → CLS → INP; each is downstream of the previous
- LCP quick wins: convert images to WebP/AVIF, add
fetchpriority="high"to the hero image, set explicit width/height - CLS quick wins: set width and height on all images, reserve space for ads, preload critical fonts
- Use Google Search Console’s Core Web Vitals report for real-world field data — that’s what Google ranks on, not Lighthouse lab scores
Frequently Asked Questions
Why is my Lighthouse score good but my Search Console score is poor?
Lighthouse runs a synthetic lab test on your machine under controlled conditions. Search Console shows field data from real Chrome users — different devices, different network speeds, different geographic locations. Real users on slow connections and older Android phones will always produce worse scores than your fast laptop on a good connection. Fix for the field data, not the lab score.
Does improving Core Web Vitals directly improve my Google ranking?
Yes, but not in isolation. Google uses CWV as a tiebreaker between pages with comparable content quality and relevance. A page with excellent content and poor CWV will still outrank a fast page with weak content. The impact is most visible in competitive niches where many pages have similar content quality.
What’s the fastest way to improve LCP?
Check what element Google is flagging as the LCP element in PageSpeed Insights. It’s usually a hero image. Convert it to WebP or AVIF, add fetchpriority="high" to the <img> tag, set explicit width and height attributes, and make sure it’s not being lazy-loaded. These four changes typically cut LCP by 30–60%.
What to Read Next
- UX Laws Every Designer Should Know — performance is one part of UX; these principles cover the rest
- Tailwind CSS Cheat Sheet — build the performant UI after you’ve fixed the metrics