Aller au contenu principal
All articles
Performance

Core Web Vitals 2025: INP, LCP, CLS — the field guide

INP replaces FID. LCP gets stricter. CLS stays stable. The field guide for getting every site into the green without wrecking the UX.

VALRY LABS Engineering TeamMarch 10, 202510 min read

INP: the new metric that changes everything

Interaction to Next Paint (INP) replaced FID in March 2024. Where FID only measured the first input delay, INP measures the full latency of all interactions across the page's lifecycle.

The 'Good' threshold is < 200 ms (vs 100 ms for FID). That sounds permissive, but in practice it's stricter: it's no longer enough to have a fast first input — EVERY interaction must be smooth.

Main causes of high INP: long click handlers (heavy computation, blocking API calls), massive JS hydration, third-party scripts hogging the main thread, unoptimised JS animations.

Lowering INP: 5 techniques that work

1. Move heavy computation off the main thread: Web Workers for complex transformations, `requestIdleCallback` for deferrable tasks.

2. Avoid `setTimeout(0)` and other 'yield hacks'. Prefer `scheduler.yield()` (Chrome 129+) which cleanly hands control back to the browser.

3. Split JavaScript into chunks (per-route code splitting, dynamic imports for heavy non-critical components). On Next.js, use `dynamic(() => import('...'), { ssr: false })` for non-essential islands.

4. Third parties: always defer or async, `requestIdleCallback` for non-critical analytics scripts, or even Partytown to offload them into a Web Worker.

5. Reduce hydration: React Server Components (Next.js 15) hydrate less DOM, which frees the main thread for interactions.

LCP: getting under 2 seconds

LCP (Largest Contentful Paint) stays at 2.5 s for the 'Good' threshold, but Google has signalled a move towards 2 s for 2025-2026. Target 2 s right now.

On Next.js, LCP optimisation comes down to: preloading the hero image (`priority` on `next/image`), self-hosting fonts (next/font), preconnecting to third-party domains (`<link rel="preconnect">`), removing critical render-blocking CSS.

On the VALRY LABS hero: AVIF image (with WebP fallback), self-hosted fonts (geist), and an animated aurora background that doesn't wait for JS to start. Result: 0.9 s on 4G mobile.

The classic trap: carousels. A carousel delays LCP because the browser doesn't know which slide to show first. Prefer a strong static image; the carousel can come later.

CLS: visual stability

CLS < 0.05 is achievable on any site with a bit of discipline. The principle: reserve space for dynamic elements BEFORE they load.

Images and videos: always specify `width` and `height` (or `aspect-ratio` in CSS). `next/image` handles this automatically when you provide the dimensions.

Cookie banners, pop-ins, ads: reserve their slot from the first paint — no shifting after the fact. Cookie banners docked at the bottom (no content shift) beat modals.

Fonts: `font-display: swap` avoids FOIT but can cause CLS if the fallback font has different metrics. Use `size-adjust` or `font-face` `ascent-override` to align them.

Measurement: lab vs field

Lighthouse and PageSpeed Insights measure in the lab (simulation). Search Console and CrUX measure in the field (real user data). Both matter, and they don't always agree.

Rule: optimise in the lab first to identify the axes of work, then validate in the field with CrUX. Field data is what counts for Google SEO.

In production, install the `web-vitals` JS library to report real metrics to your analytics (Plausible, GA4, or in-house). Track p75 percentiles, not averages.

Key takeaways

Key points.

  • INP replaces FID: all interactions must be < 200 ms, not just the first one.
  • LCP: target 2 s, self-host fonts, preload the hero, eliminate carousels.
  • CLS < 0.05: reserve space for every dynamic element (images, fonts, banners).
  • Lab (Lighthouse) to diagnose, field (CrUX) to validate SEO.
  • Report real web-vitals to your analytics, track p75.
Core Web VitalsINPLCPPerformance