Core Web Vitals have been part of Google’s ranking signals since 2021, but more importantly they’re good proxies for real user experience. Here’s what each metric actually captures and what moves the needle.

The Three Metrics

Largest Contentful Paint (LCP)

What it measures: How long until the largest visible element in the viewport is fully rendered.

Good threshold: Under 2.5 seconds.

LCP is your headline loading metric. The “largest element” is typically a hero image, a large heading, or a video poster. Chrome DevTools’ Performance tab will show you exactly which element is being measured on your page.

Common causes of poor LCP:

  • Slow server response — TTFB above ~600ms directly delays LCP
  • Render-blocking resources — CSS and synchronous JS in <head> delay the first render
  • Unoptimized images — oversized images, missing width/height attributes (triggers layout), or formats that take longer to decode
  • No preload for LCP image — the browser discovers hero images late in the load waterfall

Quick wins:

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

<!-- Add fetchpriority hint -->
<img src="/images/hero.webp" fetchpriority="high" alt="...">

Interaction to Next Paint (INP)

What it measures: The latency of the slowest interaction during a page visit — from user input (click, tap, keypress) to the next visual update.

Good threshold: Under 200 milliseconds.

INP replaced First Input Delay (FID) in March 2024. FID only measured the delay before the browser started processing an event; INP measures the full interaction latency including processing and rendering.

Common causes of poor INP:

  • Long tasks on the main thread — anything that keeps the main thread busy for 50ms+ will delay interaction handling
  • Excessive JavaScript — large bundles, unoptimized third-party scripts, and synchronous work during event handlers
  • Forced synchronous layouts — reading layout properties (like offsetHeight) immediately after DOM mutations triggers expensive recalculations

Debugging approach: use the Chrome DevTools Performance panel to record an interaction, then look for long tasks and layout thrashing.

Cumulative Layout Shift (CLS)

What it measures: The total visual instability of a page — how much content moves around unexpectedly as the page loads.

Good threshold: Under 0.1.

CLS is scored as the sum of individual layout shift scores (impact fraction × distance fraction) excluding shifts caused by user interaction.

The classic CLS culprit is images without explicit dimensions:

<!-- Bad: browser doesn't know the height until image loads -->
<img src="photo.jpg" alt="...">

<!-- Good: browser reserves space -->
<img src="photo.jpg" width="800" height="600" alt="...">

Other common causes:

  • Web fonts causing FOUT/FOIT — text reflow when fonts load
  • Ads and embeds without reserved space — late-injected content that pushes other elements down
  • Animations that affect layout properties — animating height, margin, or top instead of transform

Measuring in the Field

Lab tools (Lighthouse, PageSpeed Insights) give you a snapshot, but field data from real users is what Google actually uses. The Chrome User Experience Report (CrUX) provides 28-day rolling field data at the URL and origin level.

For static sites specifically, LCP and CLS are usually straightforward to optimize. INP is less of a concern on content-heavy pages with minimal JS, but can become an issue as interactivity increases.

For Hugo Sites

Hugo-generated sites have a natural advantage: no client-side rendering overhead, minimal JavaScript, and full control over the HTML output. The main things to focus on:

  1. Image optimization — use Hugo’s built-in image processing (images.Resize, images.Fit) to generate appropriately sized WebP images with correct dimensions
  2. Font loading — use font-display: swap and preload critical fonts
  3. Preload LCP image — identify your LCP element and add a <link rel="preload"> for it