SEO Kickoff

Core Web Vitals: How to Measure, Diagnose, and Fix Every Issue

11 min read
Core Web Vitals: How to Measure, Diagnose, and Fix Every Issue

Core Web Vitals became a Google ranking signal in 2021 and have grown in importance with every update since. They measure three aspects of page experience: loading speed, interactivity, and visual stability. All three need to be in the "Good" range to earn the page experience signal.


The Three Metrics

MetricMeasuresGoodNeeds ImprovementPoor
LCP (Largest Contentful Paint)Loading speed≤ 2.5 s2.5–4.0 s> 4.0 s
INP (Interaction to Next Paint)Responsiveness≤ 200 ms200–500 ms> 500 ms
CLS (Cumulative Layout Shift)Visual stability≤ 0.10.1–0.25> 0.25

INP replaced FID (First Input Delay) as a Core Web Vital in March 2024. It's a more complete measure of responsiveness because it captures all interactions during the page lifecycle, not just the first one.


Where to Measure

Field Data (Real Users)

  • Google Search Console → Core Web Vitals — shows page groups failing in the real world
  • Chrome User Experience Report (CrUX) — 28-day rolling data from Chrome users
  • PageSpeed Insights — combines lab + field data for a single URL

Lab Data (Simulated)

  • Lighthouse (built into Chrome DevTools) — consistent, reproducible scores
  • WebPageTest — detailed waterfall, filmstrip, multi-location testing

Always prioritise field data. Lab scores are useful for diagnosis but your ranking is determined by what real users experience.


LCP: Diagnosing and Fixing Slow Load

LCP is almost always the most impactful metric to improve. The LCP element is typically:

  • A hero image
  • A large text block
  • A video poster image

Common LCP Causes and Fixes

Slow server response (TTFB)

The single biggest lever. If your server takes 800 ms to respond, your LCP can never be under 2.5 s.

Fixes:

  • Use a CDN (Cloudflare, Fastly) to serve cached responses from edge nodes
  • Enable HTTP/2 or HTTP/3 on your server
  • Use ISR or static generation in Next.js instead of server-rendering on every request

Unoptimised LCP image

<!-- Bad: browser discovers image late, then fetches it -->
<img src="/hero.jpg" />

<!-- Good: preload hint tells browser immediately -->
<link rel="preload" as="image" href="/hero.jpg" fetchpriority="high" />
<img src="/hero.jpg" fetchpriority="high" />

In Next.js, use priority on the above-the-fold <Image> component:

<Image src="/hero.jpg" alt="Hero" fill priority />

Render-blocking resources

CSS and synchronous JS in <head> delay LCP. Audit with the Lighthouse "Eliminate render-blocking resources" diagnostic.


INP: Diagnosing and Fixing Poor Responsiveness

INP measures the time from user input (click, tap, keypress) to the next frame being painted. High INP is caused by long tasks on the main thread.

Diagnose with Chrome DevTools

  1. Open DevTools → Performance panel
  2. Record while interacting with the page
  3. Look for long tasks (red corner in the flame chart) during interactions

Common INP Causes and Fixes

Heavy event handlers

Break up synchronous work:

// Bad: 300 ms of synchronous work on click
button.addEventListener("click", () => {
  doHeavyCalculation(); // blocks the main thread
  updateUI();
});

// Good: yield to the browser between tasks
button.addEventListener("click", async () => {
  updateUI(); // immediate visual feedback
  await scheduler.yield(); // yield before heavy work
  doHeavyCalculation();
});

Third-party scripts

Analytics, chat widgets, and ad scripts often run expensive tasks during interactions. Load them with defer or after user interaction:

<script src="analytics.js" defer></script>

Large React re-renders

Use React.memo, useMemo, and useCallback to prevent unnecessary re-renders on interaction.


CLS: Diagnosing and Fixing Layout Shifts

CLS is caused by elements moving after they've been painted. The most common culprits:

Images Without Dimensions

<!-- Causes CLS: browser doesn't know height until image loads -->
<img src="/photo.jpg" alt="Photo" />

<!-- Prevents CLS: space reserved before image loads -->
<img src="/photo.jpg" alt="Photo" width="800" height="450" />

In Next.js, <Image> requires width and height (or fill with a sized container), so this is handled automatically.

Ads and Embeds Without Reserved Space

Always give ad slots a minimum height:

.ad-slot {
  min-height: 250px; /* reserve space before ad loads */
}

Web Fonts

Use font-display: optional or swap and preload your critical fonts:

<link rel="preload" href="/fonts/inter.woff2" as="font" crossorigin />

Prioritisation Framework

Not all pages matter equally. Focus effort in this order:

  1. Homepage and landing pages — highest traffic, direct impact on conversions
  2. Top-10 organic traffic pages — already ranking; CWV improvement can push them higher
  3. Product/category pages (e-commerce) — directly tied to revenue
  4. Blog posts — usually already fast if images are optimised

Tracking Progress

Set up a baseline before making changes:

  1. Export current CWV data from GSC
  2. Make one change at a time
  3. Wait 28 days for CrUX data to update
  4. Compare field data before and after

Core Web Vitals improvement is cumulative — no single fix gets you from red to green. Stack the wins: faster hosting, optimised images, deferred scripts, reserved ad space. Each one moves the needle.

Related Articles