Core Web Vitals: Measure and Improve Web Performance
You've built a modern web app, but users complain it feels slow. Search rankings might suffer too. Core Web Vitals (CWV) are Google's metrics for user experience: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). This guide shows you how to measure them accurately and improve them effectively.
What Are Core Web Vitals?
Core Web Vitals are three metrics that quantify key aspects of user experience:
- LCP measures loading performance. It marks when the largest content element (image, text block, video) becomes visible. Aim for ≤2.5 seconds.
- INP measures responsiveness. It captures the latency of all interactions (clicks, taps, key presses) and reports the worst-case (or near worst) delay. Aim for ≤200 milliseconds.
- CLS measures visual stability. It quantifies unexpected layout shifts during loading. Aim for ≤0.1.
These metrics are field-measured (from real users) and lab-measured (in controlled environments). Both matter.
How to Measure Core Web Vitals
You need both field and lab data to diagnose issues.
Field Data (Real User Monitoring)
Field data reflects actual user experiences across devices and networks. Use these sources:
- Chrome User Experience Report (CrUX): Public dataset of real-user metrics for millions of sites. Access via PageSpeed Insights or the CrUX API.
- Google Search Console: The Core Web Vitals report shows URL groups that need attention based on CrUX data.
- Web Vitals JavaScript library: Add the
web-vitalslibrary to your site and send measurements to your analytics endpoint.
import {onLCP, onINP, onCLS} from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
navigator.sendBeacon('/analytics', body);
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
Lab Data (Synthetic Testing)
Lab tools simulate loading in a controlled environment, making them great for debugging and CI integration.
- Lighthouse: Built into Chrome DevTools, available as a CLI, and integrated into PageSpeed Insights. It reports lab metrics and opportunities.
- WebPageTest: Advanced testing with custom network conditions and filmstrips.
- Chrome DevTools Performance panel: Record and analyze runtime performance, including long tasks that affect INP.
Best practice: Use lab data to identify fixes, and field data to validate impact.
Improving Largest Contentful Paint (LCP)
LCP is usually dominated by the hero image or a large text block. Optimize these:
- Optimize the LCP resource: Compress images (use WebP/AVIF), serve responsive sizes with
srcset, and preload the LCP image if it's critical. - Eliminate render-blocking resources: Inline critical CSS, defer non-critical CSS and JavaScript.
- Improve server response time (TTFB): Use a CDN, enable caching, and optimize backend queries.
- Reduce client-side rendering: For SPAs, consider server-side rendering (SSR) or static generation for the initial view.
Example: Preload the LCP image:
<link rel="preload" as="image" href="/hero.avif" imagesrcset="/hero-480.avif 480w, /hero-800.avif 800w" imagesizes="(max-width: 600px) 480px, 800px">
Improving Interaction to Next Paint (INP)
INP measures the delay between a user interaction and the next paint. Long tasks and heavy JavaScript are common culprits.
- Break up long tasks: Use
setTimeout,requestIdleCallback, or the scheduler API to yield to the main thread. - Optimize event handlers: Debounce or throttle expensive handlers; avoid layout thrashing.
- Reduce JavaScript execution time: Code-split, tree-shake, and defer non-critical scripts.
- Use web workers for CPU-intensive work.
Example: Yield to the main thread:
async function yieldToMain() {
if ('scheduler' in window && 'yield' in scheduler) {
return scheduler.yield();
}
return new Promise(resolve => setTimeout(resolve, 0));
}
async function handleClick() {
// ... do some work
await yieldToMain();
// ... continue
}
Improving Cumulative Layout Shift (CLS)
CLS occurs when elements shift unexpectedly. Prevent it by:
- Always include width and height attributes on images and videos, or use CSS
aspect-ratio. - Reserve space for dynamic content (ads, embeds, iframes) with min-height or placeholders.
- Avoid inserting content above existing content unless triggered by user interaction.
- Use
font-display: optionalor preload fonts to avoid FOIT/FOUT shifts.
Example: Reserve space for an ad:
<div>
<!-- ad loads here -->
</div>
Tools and Workflow for Continuous Improvement
Performance is not a one-time fix. Integrate measurement into your workflow:
- Set budgets: Use Lighthouse CI to enforce thresholds in pull requests.
- Monitor field data: Set up dashboards with CrUX or your own RUM.
- Prioritize fixes: Focus on metrics with the largest gap to 'good' thresholds.
- Test on real devices: Emulate mid-tier mobile devices to catch issues.
For a quick check, run your page through PageSpeed Insights; it combines lab and field data.
FAQ
What are the current thresholds for Core Web Vitals?
LCP: good ≤2.5s, needs improvement ≤4s, poor >4s. INP: good ≤200ms, needs improvement ≤500ms, poor >500ms. CLS: good ≤0.1, needs improvement ≤0.25, poor >0.25.
How often is CrUX data updated?
CrUX data is updated daily, but the dataset reflects a rolling 28-day window. Search Console updates periodically.
Can I improve Core Web Vitals without changing my design?
Yes. Many improvements are technical: optimizing images, deferring scripts, reserving space for dynamic content, and reducing main-thread work.
Ready to analyze your site's performance? Use our Nginx log analyzer to identify slow server responses that impact TTFB and LCP.