Skip to content
Blog · SEO

Core Web Vitals: the complete 2026 playbook

Optimize LCP, CLS and INP for SEO: where to start, the real traps (fonts, scheduler.yield), and the numbers we actually measured.

By Flavien · 5 min read ·

Your site feels fast. It paints crisp, scrolling is smooth, and you’re proud of it. Yet in Search Console the “Page Experience” report is red, your rankings are slipping, and you can’t figure out why. Here’s the gap: the speed you perceive at your desk — fiber connection, recent machine — has nothing to do with what Google measures, which is the real field, on a mid-range phone over a degraded network. Since INP replaced FID in March 2024, that gap is expensive. A main thread jammed for 400ms after a tap is invisible in a demo, but it wrecks your INP — and therefore your ranking.

Here’s how to close that gap, where to start, and the traps that waste your time.

Where to start: LCP first

If you could only fix one metric, make it LCP. Three concrete reasons: on an unoptimized site it’s almost always the furthest from its target, it depends on levers you control directly (images, render-blocking CSS, hosting), and it correlates best with perceived speed. Get LCP stable, and both CLS and INP get easier to deal with afterward.

LCP — Largest Contentful Paint

Target: < 2.5s in the field (CrUX, 75th percentile)

LCP measures the moment the largest visible element — usually a hero image or the main text block — is rendered.

In order of impact:

  • Identify the actual LCP element (DevTools > Performance flags it) before optimizing blindly.
  • Preload that resource with <link rel="preload"> — and only that one.
  • Serve modern formats: AVIF, with WebP as a fallback.
  • Kill render-blocking CSS and JS at the top of the document; inline the critical CSS.
  • Serve from a CDN close to the user.

CLS — Cumulative Layout Shift

Target: < 0.1

CLS measures unexpected visual shifts: the button that jumps out from under your cursor because an image or an ad pushed the content down.

Main causes:

  • Images and iframes without explicit dimensions (width/height or aspect-ratio).
  • Ad slots with no reserved space.
  • Content injected above existing content (banners, notices).
  • Mishandled web-font swapping — the most misunderstood one.

The font trap. You’ll often read that font-display: swap “causes” CLS. That’s wrong. The shift happens because the fallback font and the web font have different metrics (x-height, advance width, line height): when the web font replaces the system font, the text reflows and pushes the layout around. swap only makes that substitution visible. The real fix isn’t to ban swap — it’s to align the metrics:

  • Declare a local fallback font with size-adjust, ascent-override and descent-override so it occupies exactly the space the web font will.
  • Or use font-display: optional, which drops the web font if it doesn’t arrive in time — zero shift.
  • And preload the critical font (<link rel="preload" as="font" crossorigin>) to shrink the substitution window.

INP — Interaction to Next Paint

Target: < 200ms

The successor to FID, INP measures how responsive the page is to every interaction (clicks, taps, keystrokes), not just the first one. It’s the metric that punishes heavy client-side JavaScript.

Optimizations:

  • Break up long tasks (any JS task > 50ms blocks the response to an interaction).
  • Cut the JavaScript you ship to the client — this is exactly where an architecture that ships little JS by default, like Astro, changes the game.
  • Optimize event handlers: delegate, debounce, move heavy work off the critical path.

On scheduler.yield() — with a caveat. This API lets you hand control back to the browser in the middle of a long task while keeping priority when you resume. Elegant, but still recent and only partially supported: it isn’t available across all stable browsers yet. Don’t ship it without a fallback. A robust pattern:

  • Feature-detect it (if ('scheduler' in window && 'yield' in scheduler)) and provide an alternative.
  • Chunk the work with setTimeout(…, 0) to yield between batches.
  • Schedule non-urgent work via requestIdleCallback.
  • Pair it with navigator.scheduling.isInputPending() to bail out the moment a user interaction comes in.

Measurement tools

  1. PageSpeed Insights: field data (CrUX) plus lab data in a single view.
  2. Chrome DevTools: the Performance panel to hunt down Long Tasks and the LCP element.
  3. web-vitals.js: the official library for measuring in production, on your real users.
  4. Search Console: the “Page Experience” report — your aggregated field data.

Rule of method: lab data is for diagnosing, field data (CrUX) is what counts for ranking. Optimize in the lab, but validate in the field.

What it looks like when it’s done seriously

We don’t preach in a vacuum. After our WordPress → Astro migration, our home page — measured with the same protocol (Lighthouse mobile, Slow 4G, median of 3 runs) — posts:

  • LCP 2.42s (target < 2.5s) ✓
  • CLS 0.000 (target < 0.1) ✓
  • TBT 0ms — a solid lab proxy for INP: the main thread stays free between interactions.

The full migration story and the before/after comparison are in Why we chose Astro.

For every project, we audit Core Web Vitals before delivery and aim for Performance, Accessibility and SEO above 95, with Best Practices at 100. That’s not perfectionism — it’s what separates a site that feels fast from a site that’s fast for Google.

Stuck on your Core Web Vitals?

If Search Console is red and you don’t know where to start, we can diagnose it. Request a performance audit: we’ll pinpoint your LCP elements, your CLS sources and your long tasks, and hand you a prioritized action plan — LCP first.

Have a project in mind?

Let's discuss it over a free first consultation.

Let's talk about your project