Skip to main content

Core Web Vitals Debugging Guide

Debug and optimize LCP, INP, and CLS — Google's Core Web Vitals. Covers Chrome DevTools techniques, the web-vitals library, real user monitoring, and the most impactful fixes.

✓ LCP / INP / CLS ✓ Chrome DevTools techniques ✓ Real User Monitoring ✓ Actionable fixes

Core Web Vitals Quick Reference

Metric Measures Good Poor
LCPLargest element renders< 2.5s> 4s
INPInteraction responsiveness< 200ms> 500ms
CLSLayout stability< 0.1> 0.25
FCPFirst content paints< 1.8s> 3s
TTFBServer response time< 800ms> 1.8s

Measuring Vitals with the web-vitals Library

npm install web-vitals
// vitals.ts — send metrics to your analytics endpoint
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';

function sendToAnalytics(metric: object) {
  // Send to your backend, Google Analytics, or Datadog
  navigator.sendBeacon('/api/vitals', JSON.stringify(metric));
}

onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

// Next.js: add to app/layout.tsx or pages/_app.tsx
// export { reportWebVitals } from 'next/dist/pages/_app'
// function reportWebVitals(metric) { sendToAnalytics(metric) }

LCP Optimization Techniques

<!-- 1. Preload the LCP image -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">

<!-- 2. Mark the LCP image with fetchpriority -->
<img src="/hero.webp" fetchpriority="high" loading="eager" alt="Hero"
     width="1200" height="600">

<!-- 3. Avoid lazy-loading above-the-fold images -->
<!-- BAD: -->
<img src="/hero.webp" loading="lazy">  <!-- NEVER lazy-load LCP -->
<!-- GOOD: -->
<img src="/hero.webp" loading="eager" fetchpriority="high">
// Next.js Image component — automatically adds fetchpriority="high"
import Image from 'next/image';

<Image
  src="/hero.webp"
  priority    // ← This is the key prop for LCP images
  width={1200}
  height={600}
  alt="Hero"
/>

INP Optimization — Reducing Long Tasks

// Identify long tasks in Chrome DevTools:
// Performance tab → record → look for red triangles on tasks > 50ms

// Pattern 1: Break up long synchronous work with scheduler.yield()
async function processLargeList(items: Item[]) {
  for (let i = 0; i < items.length; i++) {
    processItem(items[i]);
    // Yield every 50 items to avoid blocking the main thread
    if (i % 50 === 0) {
      await scheduler.yield();  // releases main thread briefly
    }
  }
}

// Pattern 2: Defer non-critical work
// BAD: blocking third-party script in <head>
<script src="https://analytics.example.com/script.js"></script>

// GOOD: defer + async
<script src="https://analytics.example.com/script.js" defer async></script>

// Pattern 3: Use web workers for CPU-heavy computation
const worker = new Worker('/workers/heavy-computation.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => setResult(e.data.result);

CLS Fixes

<!-- Fix 1: Always specify image dimensions -->
<!-- BAD: no dimensions = layout shift when image loads -->
<img src="/photo.jpg" alt="Photo">
<!-- GOOD: browser reserves space before image loads -->
<img src="/photo.jpg" alt="Photo" width="800" height="600">

<!-- Fix 2: Reserve space for ads and embeds -->
<div style="min-height: 250px">
  <!-- ad loads here without shifting content below -->
  <ins class="adsbygoogle" style="display:block" ...></ins>
</div>

/* Fix 3: Font display swap to prevent FOUT layout shift */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2');
  font-display: optional;  /* never causes layout shift */
}

/* Fix 4: Aspect ratio container for responsive media */
.video-wrapper {
  aspect-ratio: 16 / 9;  /* reserves correct space before video loads */
}

DevTools Workflow

Frequently Asked Questions

What are Core Web Vitals and why do they affect SEO?▼

Core Web Vitals are Google's standardized set of user experience metrics used as ranking signals: LCP (Largest Contentful Paint) measures loading speed, INP (Interaction to Next Paint, replaced FID in 2024) measures responsiveness to user input, and CLS (Cumulative Layout Shift) measures visual stability. Sites with poor Core Web Vitals can be demoted in search rankings. Google uses field data from Chrome User Experience Report (CrUX) — real user measurements — not lab data from Lighthouse.

What is a good LCP score and how do I improve it?▼

Good LCP: under 2.5 seconds. Needs improvement: 2.5-4 seconds. Poor: over 4 seconds. The main LCP element is usually a hero image, above-the-fold heading, or large text block. To improve LCP: add fetchpriority='high' to the LCP image, preload critical images with <link rel=preload>, serve images in WebP/AVIF format, use a CDN to reduce server response time, and eliminate render-blocking resources (unused CSS/JS) that delay LCP element rendering.

How do I diagnose and fix Cumulative Layout Shift (CLS)?▼

CLS occurs when elements shift position after the page loads. The most common causes are: images without explicit width/height attributes (reserve space by always setting dimensions), ads and embeds that inject content above existing elements, web fonts causing a Flash of Unstyled Text (FOUT) that shifts layout. To debug: open Chrome DevTools > Performance > record a page load and look for Layout Shift entries. Use the Layout Instability API or the Web Vitals Chrome extension to identify which elements shift.

What is INP and how is it different from FID?▼

INP (Interaction to Next Paint) replaced FID (First Input Delay) as a Core Web Vital in March 2024. FID measured only the delay before the browser starts processing the first user interaction. INP measures the full duration from user input to the next visual frame across all interactions during the page lifetime — it's a much more complete measure of responsiveness. Good INP: under 200ms. Poor INP: over 500ms. Long tasks on the main thread are the primary INP culprit.

How do I measure real user Core Web Vitals (field data)?▼

Use the web-vitals JavaScript library (npm install web-vitals) to collect metrics from real users and send them to your analytics. Call onLCP, onINP, onCLS, onFCP, onTTFB with a callback that sends the metric to your endpoint. Google Search Console shows aggregated field data from CrUX. PageSpeed Insights shows both lab (Lighthouse) and field data. Field data typically differs from Lighthouse because it reflects real devices, real networks, and real user behavior.

Why does my Lighthouse score look good but field data shows poor Core Web Vitals?▼

Lab data (Lighthouse) simulates a mid-tier device with throttled network in a controlled environment. Field data comes from real users on real devices across all connection types. Common gaps: your users are on slow mobile devices (Lighthouse uses a Moto G4 simulation), your JS bundle is large and blocks interaction on low-end hardware, third-party scripts (ads, chat widgets, analytics) add significant weight in production but may be absent in local testing, or your CDN cache is warm in tests but cold for new users.

Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →