Core Web Vitals Optimization
Optimize the three Core Web Vitals that Google uses for search ranking and user experience. Covers Largest Contentful Paint, Cumulative Layout Shift, Interaction to Next Paint, measurement tools, and the performance engineering patterns that deliver fast, stable, responsive pages.
Core Web Vitals are Google’s three metrics for measuring real user experience. They directly impact search rankings and, more importantly, conversion rates. Every 100ms of delay loses 1% of revenue. Every unexpected layout shift loses user trust. CWV optimization is not optional — it is the price of being competitive on the web.
The Three Metrics
LCP (Largest Contentful Paint): < 2.5 seconds
"How fast does the main content appear?"
Measures: Largest image, video, or text block visible in viewport
INP (Interaction to Next Paint): < 200ms
"How responsive is the page to user input?"
Measures: Time from click/tap to visual response
CLS (Cumulative Layout Shift): < 0.1
"How stable is the layout?"
Measures: Unexpected movements of visible elements
LCP Optimization
<!-- Preload critical resources -->
<link rel="preload" href="/hero-image.webp" as="image" fetchpriority="high">
<link rel="preload" href="/fonts/inter-var.woff2" as="font" crossorigin>
<!-- Inline critical CSS -->
<style>
/* Only above-the-fold styles */
.hero { min-height: 60vh; display: flex; align-items: center; }
.hero-image { width: 100%; height: auto; aspect-ratio: 16/9; }
</style>
<!-- Defer non-critical CSS -->
<link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
<!-- Responsive images with proper sizing -->
<img
src="/hero-800.webp"
srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
alt="Hero image"
width="1200" height="675"
loading="eager"
fetchpriority="high"
decoding="async"
>
LCP Checklist:
☐ Hero image preloaded with fetchpriority="high"
☐ Critical CSS inlined (< 14KB)
☐ Fonts preloaded or use font-display: swap
☐ Server response < 200ms (TTFB)
☐ Images in WebP/AVIF format
☐ CDN for static assets
☐ No render-blocking JavaScript
CLS Optimization
/* Always set dimensions on media */
img, video {
width: 100%;
height: auto;
aspect-ratio: attr(width) / attr(height);
}
/* Reserve space for dynamic content */
.ad-slot {
min-height: 250px; /* Reserve ad space */
}
.skeleton {
min-height: 200px; /* Reserve skeleton space */
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
/* Prevent font swap layout shift */
@font-face {
font-family: 'Inter';
font-display: optional; /* Don't swap if font loads late */
src: url('/fonts/inter.woff2') format('woff2');
}
CLS Checklist:
☐ All images have width/height or aspect-ratio
☐ Ads/embeds have reserved space
☐ No dynamically injected content above fold
☐ font-display: optional or swap with matching fallback
☐ No late-loading CSS that changes layout
INP Optimization
// Break up long tasks (> 50ms) to stay responsive
async function processLargeDataset(items) {
const CHUNK_SIZE = 100;
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
const chunk = items.slice(i, i + CHUNK_SIZE);
processChunk(chunk);
// Yield to browser between chunks
await new Promise(resolve => setTimeout(resolve, 0));
}
}
// Use requestIdleCallback for non-urgent work
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0) {
doNonUrgentWork();
}
});
// Debounce expensive event handlers
function debounce(fn, delay = 150) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| No image dimensions | CLS from images loading | Always set width/height/aspect-ratio |
| Render-blocking JS in head | LCP delayed | defer/async or move to body end |
| Synchronous third-party scripts | INP blocked by external code | Lazy load, web workers |
| No CDN | High TTFB globally | Edge CDN for static+dynamic |
| Lab-only testing | Misses real user experience | Real User Monitoring (RUM) |
Core Web Vitals are the bridge between engineering performance and business performance. Every millisecond you save converts directly into revenue, engagement, and search visibility.