Introduction
Core Web Vitals are Google's standardized metrics for measuring real-world user experience on the web. They focus on three aspects: loading performance (LCP), interactivity (INP), and visual stability (CLS). These metrics directly impact SEO rankings—Google uses them as ranking signals. This guide explains each metric, how to measure them, and practical optimization strategies.
The Three Core Web Vitals
Largest Contentful Paint (LCP)
LCP measures how long it takes for the largest content element to become visible:
| Rating | LCP |
|---|---|
| Good | ≤ 2.5s |
| Needs Improvement | 2.5s - 4s |
| Poor | > 4s |
import { onLCP } from 'web-vitals';
onLCP((metric) => {
console.log('LCP:', metric.value);
console.log('LCP element:', metric.element);
// Send to analytics
sendToAnalytics('LCP', metric);
});Interaction to Next Paint (INP)
INP measures responsiveness—the time from user interaction to the next visual update:
| Rating | INP |
|---|---|
| Good | ≤ 200ms |
| Needs Improvement | 200ms - 500ms |
| Poor | > 500ms |
import { onINP } from 'web-vitals';
onINP((metric) => {
console.log('INP:', metric.value);
console.log('Interaction target:', metric.element);
sendToAnalytics('INP', metric);
});Cumulative Layout Shift (CLS)
CLS measures visual stability—how much elements shift during page load:
| Rating | CLS |
|---|---|
| Good | ≤ 0.1 |
| Needs Improvement | 0.1 - 0.25 |
| Poor | > 0.25 |
import { onCLS } from 'web-vitals';
onCLS((metric) => {
console.log('CLS:', metric.value);
sendToAnalytics('CLS', metric);
});Optimization Strategies
Improving LCP
// 1. Preload critical resources
// In HTML head
<link rel="preload" as="image" href="/hero.webp" />
<link rel="preload" as="font" href="/fonts/main.woff2" crossorigin />
// 2. Use Next.js Image component
import Image from 'next/image';
function Hero() {
return (
<Image
src="/hero.webp"
alt="Hero"
width={1200}
height={600}
priority // Preload this image
/>
);
}
// 3. Server-side rendering
// Fetch data on the server, not in useEffect
async function ProductPage({ id }) {
const product = await fetchProduct(id); // Server-side
return <Product product={product} />;
}Improving INP
// 1. Break up long tasks
function processLargeDataset(items: Item[]) {
// Bad: blocks main thread
// items.forEach(processItem);
// Good: yields to main thread
async function processInChunks() {
const chunkSize = 100;
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
chunk.forEach(processItem);
await new Promise(resolve => setTimeout(resolve, 0));
}
}
processInChunks();
}
// 2. Use scheduler.yield()
async function handleClick() {
// Process first part
updateUI('loading');
await scheduler.yield(); // Let browser paint
// Process rest
const result = await heavyComputation();
updateUI('done', result);
}Improving CLS
// 1. Always set dimensions on images/videos
<img src="photo.jpg" width="800" height="600" alt="Photo" />
// 2. Reserve space for dynamic content
function AdBanner() {
return (
<div style={{ minHeight: '250px' }}>
{/* Ad content loads here without shifting layout */}
</div>
);
}
// 3. Use CSS aspect-ratio
.hero-image {
aspect-ratio: 16 / 9;
width: 100%;
}Measuring Performance
Using web-vitals Library
import { onLCP, onINP, onCLS } from 'web-vitals';
function sendToAnalytics(name: string, metric: any) {
const body = JSON.stringify({
name,
value: metric.value,
id: metric.id,
delta: metric.delta,
url: window.location.href,
});
// Use sendBeacon for reliable delivery
navigator.sendBeacon('/api/analytics', body);
}
onLCP(sendToAnalytics.bind(null, 'LCP'));
onINP(sendToAnalytics.bind(null, 'INP'));
onCLS(sendToAnalytics.bind(null, 'CLS'));Chrome DevTools
1. Open DevTools → Performance tab
2. Record a page load
3. Look for LCP marker in the timeline
4. Check "Experience" section for layout shifts
5. Use "Performance Insights" panel for recommendations
Best Practices
- Measure real users: Use RUM (Real User Monitoring), not just lab tests
- Set performance budgets: LCP < 2.5s, INP < 200ms, CLS < 0.1
- Monitor continuously: Set up alerts for regressions
- Test on real devices: Mobile performance differs significantly
- Optimize for your worst users: Target p75 metrics
Common Pitfalls
| Pitfall | Metric Affected | Solution |
|---|---|---|
| Unoptimized hero image | LCP | Use WebP, preload, proper sizing |
| Third-party scripts | INP | Defer loading, use partytown |
| Images without dimensions | CLS | Always set width/height |
| Late-loading fonts | CLS | Use font-display: swap |
| Client-side data fetching | LCP | Fetch server-side |
Monitoring and Alerting
Implement continuous monitoring of Core Web Vitals using Real User Monitoring tools. Google's Chrome UX Report provides aggregated field data for millions of websites, letting you compare your metrics against industry benchmarks. Set up alerts when any Core Web Vital degrades beyond your target threshold. Use synthetic monitoring tools like Lighthouse CI to catch regressions before they reach production. Integrate performance monitoring into your CI pipeline so that pull requests that degrade performance are flagged before merging. Create dashboards that show Core Web Vitals trends over time, segmented by device type, connection speed, and geographic location.
Largest Contentful Paint Optimization
Largest Contentful Paint measures how quickly the largest content element on a page becomes visible. To optimize LCP, prioritize loading the largest content element by ensuring it is discoverable early in the document. Use preload links for critical images and fonts. Implement responsive images with srcset to serve appropriately sized images for each device. Use modern image formats like WebP and AVIF that provide better compression than JPEG and PNG. Eliminate render-blocking resources by deferring non-critical CSS and JavaScript. Use server-side rendering or static generation to deliver HTML that includes the LCP element without waiting for JavaScript execution. These optimizations typically reduce LCP by fifty percent or more.
Cumulative Layout Shift Prevention
Cumulative Layout Shift measures the visual stability of a page by tracking unexpected layout movements. Common causes of layout shift include images without dimensions, dynamically injected content, web fonts causing text reflow, and ads that resize their containers. Prevent layout shift by always specifying width and height attributes on images and video elements. Reserve space for dynamic content using CSS min-height. Use font-display optional for web fonts to prevent text reflow. Implement the aspect-ratio CSS property for responsive media. Use the CSS contain property to isolate layout changes to specific elements. Regularly audit your CLS score using Lighthouse and fix any regressions immediately.
Interaction to Next Paint Deep Dive
Interaction to Next Paint replaced First Input Delay as a Core Web Vital in March twenty twenty four. While First Input Delay only measured the delay before the first interaction, INP measures the responsiveness of all interactions throughout the page lifecycle. INP is the latency of the longest interaction, excluding outliers at the ninety-eighth percentile. To optimize INP, break long JavaScript tasks into smaller chunks using scheduler.yield or requestIdleCallback. Avoid blocking the main thread during event handlers. Use web workers for CPU-intensive computations. Implement virtual scrolling for long lists to reduce the number of DOM elements that need to be processed during interactions.
Field Data vs Lab Data
Understanding the difference between field data and lab data is crucial for performance optimization. Field data represents real user experiences collected from actual devices, networks, and geographic locations. Lab data represents synthetic measurements taken under controlled conditions using tools like Lighthouse and WebPageTest. Field data is more accurate because it reflects the diversity of your user base, but it only tells you what happened, not why. Lab data is less representative but provides actionable recommendations for improvement. Use field data to measure your current performance and identify trends. Use lab data to diagnose specific issues and test optimizations before deploying them to production.
Performance Budgets
Establish performance budgets for your Core Web Vitals metrics. A performance budget sets maximum thresholds for metrics like LCP, CLS, and INP that your team commits to maintaining. Define budgets based on the good thresholds recommended by Google: LCP under two point five seconds, CLS under zero point one, and INP under two hundred milliseconds. Enforce budgets in your CI pipeline by running Lighthouse checks on every pull request and failing the build if any metric exceeds the budget. Use performance budgets to make data-driven decisions about adding new features or third-party scripts, ensuring that each addition stays within the budget.
Third-Party Script Impact
Third-party scripts like analytics, advertising, and chat widgets are often the biggest contributors to performance degradation. Each third-party script adds JavaScript that must be downloaded, parsed, and executed, consuming CPU time and delaying your Core Web Vitals metrics. Audit your third-party scripts using the Performance tab in browser developer tools to identify which scripts consume the most resources. Use the async or defer attribute on script tags to prevent render blocking. Load non-critical third-party scripts after the page has loaded using the requestIdleCallback API. Consider self-hosting critical third-party scripts to reduce DNS lookup and connection overhead. Remove any third-party scripts that do not provide measurable business value.
Page Speed and SEO
Core Web Vitals directly affect search engine rankings as part of Google's page experience signals. Pages that meet the good thresholds for all three Core Web Vitals receive a ranking boost compared to pages that do not. Monitor your Core Web Vitals using Google Search Console, which reports field data from the Chrome User Experience Report for your domain. Identify pages that fail to meet the good thresholds and prioritize fixing them. Use the PageSpeed Insights tool to get specific recommendations for each page. Track your Core Web Vitals over time to ensure that new features and content do not degrade performance. Treat Core Web Vitals optimization as an ongoing process rather than a one-time task.
Mobile Performance
Mobile devices present unique performance challenges due to slower processors, limited memory, and variable network conditions. Optimize for mobile by reducing the total amount of JavaScript that needs to be parsed and executed. Use responsive images that serve smaller versions on mobile devices. Implement touch-optimized interactions that do not rely on hover states. Minimize the use of web fonts, which are larger on mobile due to slower network connections. Test your Core Web Vitals on real mobile devices using throttled network conditions to simulate typical user experiences. Mobile optimization is particularly important because the majority of web traffic now comes from mobile devices.
Continuous Performance Improvement
Establish a culture of continuous performance improvement in your development team. Include performance reviews as part of your code review process. Set up automated performance monitoring that alerts your team when metrics degrade. Celebrate performance wins and share optimization techniques across the team. Create documentation that describes your performance budget, optimization strategies, and testing procedures. Make performance data visible to everyone through dashboards and regular reports. When performance regressions occur, treat them with the same urgency as functional bugs. A performance-focused culture ensures that your application remains fast and responsive as it grows.
Optimization Checklist
Use this checklist to optimize your Core Web Vitals. For LCP, preload critical images and fonts, use responsive images, implement server-side rendering, and eliminate render-blocking resources. For CLS, specify dimensions on all media elements, reserve space for dynamic content, use font-display optional, and avoid injecting content above existing content. For INP, break long tasks into smaller chunks, use web workers for CPU-intensive work, implement virtual scrolling, and minimize main thread work during event handlers. Test each optimization using Lighthouse and field data to verify that it improves the metric without degrading other aspects of your application.
Monitoring Core Web Vitals through real user monitoring provides the most accurate picture of how your site performs for actual visitors.
Monitoring Core Web Vitals through real user monitoring provides the most accurate picture of how your site performs for actual visitors across different devices, network conditions, and geographic locations.
Measuring Core Web Vitals in Practice
Setting Up Real User Monitoring (RUM)
Collect field data from your production users to understand real-world performance:
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
interface VitalMetric {
name: string;
value: number;
rating: 'good' | 'needs-improvement' | 'poor';
id: string;
navigationType: string;
delta: number;
}
function sendToAnalytics(metric: VitalMetric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
id: metric.id,
page: window.location.pathname,
connection: (navigator as any).connection?.effectiveType,
deviceMemory: (navigator as any).deviceMemory,
timestamp: Date.now(),
});
// Use sendBeacon for reliability (works even during page unload)
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/vitals', body);
} else {
fetch('/api/vitals', { body, method: 'POST', keepalive: true });
}
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);Lighthouse CI Integration
Automate Core Web Vitals checking in your CI pipeline to catch regressions before they reach production:
# .github/workflows/performance.yml
name: Performance Check
on: [pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci && npm run build
- uses: treosh/lighthouse-ci-action@v11
with:
urls: |
http://localhost:3000/
http://localhost:3000/blog
http://localhost:3000/about
budgetPath: ./lighthouse-budget.json
uploadArtifacts: true// lighthouse-budget.json
[
{
"path": "/*",
"timings": [
{ "metric": "largest-contentful-paint", "budget": 2500 },
{ "metric": "interactive", "budget": 3500 }
],
"resourceSizes": [
{ "resourceType": "script", "budget": 300 },
{ "resourceType": "total", "budget": 1000 }
]
}
]LCP Optimization with Priority Hints
Use the fetchpriority attribute to signal which resources are critical for LCP:
<!-- High priority for the hero image (LCP element) -->
<img src="/hero.webp" fetchpriority="high" alt="Hero" width="1200" height="600">
<!-- Low priority for below-the-fold images -->
<img src="/footer.webp" fetchpriority="low" alt="Footer" loading="lazy">
<!-- Preload the LCP font -->
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
<!-- Preload the critical CSS -->
<link rel="preload" href="/critical.css" as="style">Reducing CLS with the contain CSS Property
The CSS contain property isolates layout recalculations to specific DOM subtrees, preventing them from affecting the rest of the page:
/* Isolate layout changes to the sidebar */
.sidebar {
contain: layout style;
width: 300px;
min-height: 100vh;
}
/* Prevent ad slot from causing layout shifts */
.ad-container {
contain: layout size;
min-height: 250px; /* Standard ad height */
background: #f0f0f0;
}
/* Image container with aspect ratio */
.image-wrapper {
contain: layout;
aspect-ratio: 16 / 9;
width: 100%;
}
.image-wrapper img {
width: 100%;
height: 100%;
object-fit: cover;
}Optimizing INP with the Scheduler API
Break long-running tasks into smaller chunks to keep the main thread responsive:
// Modern approach using scheduler.yield()
async function processData(items: DataItem[]) {
const batchSize = 50;
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
processBatch(batch);
// Yield to the browser to handle pending interactions
if ('scheduler' in globalThis && 'yield' in (globalThis as any).scheduler) {
await (globalThis as any).scheduler.yield();
} else {
// Fallback for browsers without Scheduler API
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}
// Using requestIdleCallback for non-urgent work
function scheduleAnalyticsReport(data: AnalyticsData) {
if ('requestIdleCallback' in window) {
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0 && dataQueue.length > 0) {
const item = dataQueue.shift();
processAnalyticsItem(item);
}
}, { timeout: 5000 });
} else {
setTimeout(() => processAnalyticsItem(data), 0);
}
}Conclusion
Core Web Vitals are essential metrics for modern web development. They directly impact user experience and SEO rankings. By understanding LCP, INP, and CLS and applying the optimization strategies in this guide, you can deliver fast, responsive, and visually stable web experiences.
Key takeaways:
- LCP ≤ 2.5s: Optimize critical rendering path
- INP ≤ 200ms: Keep main thread free
- CLS ≤ 0.1: Reserve space for dynamic content
- Measure with web-vitals: Real user monitoring
- Performance is a ranking signal: Google uses CWV for SEO
- Automate checks: Use Lighthouse CI to catch regressions
- Use modern APIs:
fetchpriority,scheduler.yield(), andcontain