Core Web Vitals: What Google Measures
Google uses three metrics to judge your page's user experience. They affect search rankings, and more importantly, they affect whether users stay on your page or bounce.
The Three Metrics
| Metric | Measures | Good | Needs Work | Poor |
|---|---|---|---|---|
| LCP | Loading speed (largest content paint) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
| INP | Responsiveness (interaction to next paint) | ≤ 200ms | ≤ 500ms | > 500ms |
| CLS | Visual stability (cumulative layout shift) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
LCP — Largest Contentful Paint
How long until the largest visible element (hero image, heading, video) is fully rendered. Common LCP elements:
- Hero images
- Large text blocks (h1)
- Video poster images
- Background images via CSS
How to improve LCP:
<!-- Preload your LCP image -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" />
<!-- Use modern 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="630" fetchpriority="high" />
</picture>- Serve images in WebP/AVIF format (40-60% smaller)
- Use
fetchpriority="high"on the LCP element - Don't lazy-load above-the-fold images
- Use a CDN for static assets
- Minimize render-blocking CSS and JS
INP — Interaction to Next Paint
Measures how quickly the page responds to user interactions (clicks, taps, key presses). INP replaced FID (First Input Delay) in March 2024.
How to improve INP:
// Break up long tasks
// BAD: blocks the main thread for 200ms
function processData(items) {
items.forEach(item => heavyComputation(item));
}
// GOOD: yield to the browser between chunks
async function processData(items) {
for (let i = 0; i < items.length; i++) {
heavyComputation(items[i]);
if (i % 100 === 0) {
await new Promise(r => setTimeout(r, 0)); // yield
}
}
}- Keep JavaScript tasks under 50ms
- Use
requestAnimationFramefor visual updates - Debounce rapid interactions (scroll, resize)
- Move heavy computation to Web Workers
- Reduce third-party script impact
CLS — Cumulative Layout Shift
Measures unexpected layout shifts — when elements move around as the page loads. The worst offenders:
- Images without dimensions (browser doesn't know the size until loaded)
- Ads/embeds injected dynamically
- Web fonts causing FOUT (flash of unstyled text)
- Dynamic content inserted above existing content
How to improve CLS:
<!-- Always set width and height on images -->
<img src="photo.jpg" width="800" height="600" alt="Photo" />
<!-- Reserve space for ads -->
<div style="min-height: 250px;">
<!-- ad slot -->
</div>
<!-- Preload fonts to prevent layout shift -->
<link rel="preload" as="font" href="/fonts/inter.woff2"
type="font/woff2" crossorigin />/* CSS: prevent font swap layout shift */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap; /* or 'optional' for no shift at all */
}Measuring Tools
| Tool | Type | What It Tells You |
|---|---|---|
| PageSpeed Insights | Lab + Field | Real user data + lab simulation |
| Chrome DevTools (Lighthouse) | Lab | Detailed performance audit |
| Web Vitals Extension | Real-time | Live metrics as you browse |
| Search Console | Field | Real user data aggregated |
Optimize your images: Image Converter — convert to WebP/AVIF for smaller files and better LCP scores.