Core Web Vitals Guide — LCP, FID, CLS Measuring, Optimizing & Tools
In this tutorial, you'll learn about Core Web Vitals Guide. We cover key concepts, practical examples, and best practices.
Core Web Vitals are Google's set of real-world metrics that quantify user experience — loading (LCP), interactivity (FID), and visual stability (CLS) — used as ranking signals in search results.
What You'll Learn
You'll learn how to measure LCP, FID, and CLS, optimize each metric with specific techniques, use tools like Lighthouse and PageSpeed Insights, and monitor real-user data with the web-vitals library.
Why It Matters
Core Web Vitals became Google ranking factors in 2021 and continue to influence search rankings. Sites passing all three thresholds see 24% lower bounce rates. Web Performance Optimization starts with Core Web Vitals. Doda Browser includes a Web Vitals panel in its developer tools.
Real-World Use
A news publisher optimizes LCP by preloading the hero image and inlining critical CSS, reduces FID by breaking up long tasks, and fixes CLS by reserving space for ads and images — improving their search ranking from page 3 to page 1.
LCP — Largest Contentful Paint
LCP measures when the largest visible content element (image, video poster, or text block) finishes rendering. Target: under 2.5 seconds.
<!-- Optimize LCP: preload the hero image -->
<link rel="preload" href="hero-1600.webp" as="image" fetchpriority="high">
<!-- Inline critical CSS above the fold -->
<style>
/* Critical above-the-fold styles */
.hero { display: flex; align-items: center; justify-content: center; height: 80vh; }
.hero h1 { font-size: clamp(2rem, 5vw, 4rem); color: #1a1a2e; }
</style>
<link rel="stylesheet" href="/css/main.css" media="print" onload="this.media='all'">
// Measure LCP with the web-vitals library
import { onLCP } from "web-vitals";
onLCP((metric) => {
console.log(`LCP: ${metric.value}ms`);
// Send to analytics
navigator.sendBeacon("/analytics", JSON.stringify({
name: "LCP",
value: metric.value,
rating: metric.value <= 2500 ? "good" : metric.value <= 4000 ? "needs-improvement" : "poor"
}));
});
Expected behavior: The hero image preloads immediately, critical CSS blocks rendering, and the full stylesheet loads asynchronously. LCP drops from 4s to 1.5s.
FID — First Input Delay
FID measures the time from when a user first interacts with a page (click, tap, keypress) to when the browser can respond. Target: under 100ms. FID will be replaced by INP (Interaction to Next Paint) in 2024+.
// Measure FID
import { onFID } from "web-vitals";
onFID((metric) => {
console.log(`FID: ${metric.value}ms`);
if (metric.value > 100) {
// Flag long input delays
console.warn("FID exceeds threshold. Check main thread tasks.");
}
});
// Break up long tasks with scheduler.yield()
async function processUserData(data) {
const chunks = chunkArray(data, 50);
for (const chunk of chunks) {
// Yield to main thread between chunks
await new Promise(resolve => setTimeout(resolve, 0));
// Process chunk
chunk.forEach(item => updateUI(item));
}
}
function chunkArray(arr, size) {
return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
arr.slice(i * size, i * size + size)
);
}
Expected behavior: Long tasks break into 50-item chunks. The browser processes user input between chunks, keeping FID under 100ms.
CLS — Cumulative Layout Shift
CLS measures unexpected layout shifts that occur when visible elements move after the user has started interacting with the page. Target: under 0.1.
<!-- Reserve space for dynamic content -->
<div class="ad-container" style="width: 300px; height: 250px;">
<div id="ad-slot"></div>
</div>
<!-- Set explicit dimensions for images -->
<img
src="article-image.webp"
width="800"
height="450"
alt="Article image"
loading="lazy"
decoding="async"
>
<!-- Use aspect-ratio for responsive containers -->
<div class="video-wrapper" style="aspect-ratio: 16 / 9;">
<iframe src="https://www.youtube.com/embed/abc123" title="Tutorial video" allowfullscreen></iframe>
</div>
// Measure CLS
import { onCLS } from "web-vitals";
onCLS((metric) => {
console.log(`CLS: ${metric.value}`);
if (metric.value > 0.1) {
// Find elements causing layout shift
console.warn("CLS exceeds threshold. Check for missing dimensions on images and embeds.");
}
});
Expected behavior: Ads, images, and embeds have reserved space. No content shifts after the initial layout. CLS stays below 0.05.
flowchart TD A[Core Web Vitals] --> B[LCP < 2.5s] A --> C[FID < 100ms] A --> D[CLS < 0.1] B --> B1[Preload key resources] B --> B2[Minimize render-blocking] B --> B3[Optimize images] C --> C1[Code splitting] C --> C2[Break up long tasks] C --> C3[Web Workers] D --> D1[Set image dimensions] D --> D2[Reserve ad space] D --> D3[Use aspect-ratio CSS] style A fill:#48f,color:#fff style B fill:#4a4,color:#fff style C fill:#f80,color:#fff style D fill:#a4f,color:#fff
Common Errors
- Not measuring real-user data: Lighthouse data is synthetic. Real-user conditions vary. Use the
web-vitalslibrary and RUM (Real User Monitoring) to get accurate field data. - Optimizing LCP by reducing image quality too much: A 10KB LCP image loads fast but looks terrible. Use modern formats (WebP, AVIF) with adequate quality rather than extreme compression.
- Ignoring CLS from web fonts: Font swap can cause layout shifts. Use
font-display: optionalorsize-adjustto reserve font space. - Long tasks from third-party scripts: Analytics, ads, and tracking scripts often run long tasks. Load third-party scripts with
asyncand consider using a script loader with timeouts. - Forgetting mobile performance: Desktop scores often pass but mobile fails. Test Core Web Vitals on a throttled 4G connection with a mid-range device.
- CLS from dynamically injected content: Content that appears above existing elements without reservation causes shifts. Always use
position: absoluteor reserved space.
Practice Questions
- What is the difference between LCP and FCP? FCP (First Contentful Paint) marks when any content first renders. LCP marks when the largest content element renders. LCP is the more meaningful loading metric.
- Why is FID being replaced by INP? FID only measures the first interaction. INP measures all interactions throughout the page lifecycle and provides a more complete picture of responsiveness.
- What causes CLS from web fonts? When a fallback font renders first and the custom font loads later with different metrics, the text reflows. Use
font-display: optionalorsize-adjust. - How do you measure Core Web Vitals in production? Use the
web-vitalsJavaScript library, Google's crUX API, PageSpeed Insights, and RUM tools like SpeedCurve or Datadog RUM. - Challenge: A page has LCP of 4.8s (hero image), FID of 180ms (analytics script blocking), and CLS of 0.35 (ads without dimensions). Create a prioritized fix list with specific code changes and target metric values.
Mini Project
Optimize Core Web Vitals on a sample page:
- Create a test page with unoptimized images, blocking scripts, and ad placeholders
- Measure baseline LCP, FID, and CLS with Lighthouse
- Preload the hero image and inline critical CSS
- Convert images to WebP with responsive srcset
- Add
widthandheightattributes to all images - Reserve space for ad slots and embeds
- Break up long JavaScript tasks
- Add the
web-vitalslibrary for real-user monitoring - Re-run Lighthouse and verify all three metrics pass the "Good" threshold
- Set up a CrUX dashboard to monitor field data
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro