Frontend
Lesson 5 of 8About 3 min readSuggest an edit

Web performance and Core Web Vitals

Core Web Vitals are three metrics that each describe something a user feels: how fast the main content appears, how quickly the page responds, and how stable it is while loading.

The three metrics

Metric Measures Good
LCP When the largest image or text block in the viewport renders ≤ 2.5 s
INP Delay from a click, tap or key press to the next paint ≤ 200 ms
CLS How much visible content moves unexpectedly ≤ 0.1

Largest Contentful Paint (LCP) is measured from the start of navigation; the element is usually a hero image or a large heading.

Interaction to Next Paint (INP) looks at the interactions during a whole visit and reports one of the slowest. It replaced First Input Delay, which only measured the delay before the first interaction’s handler started.

Cumulative Layout Shift (CLS) scores shifts by how much of the viewport moved and how far. Shifts that happen shortly after a user’s input are excluded, because the user expected them.

A page passes when the 75th percentile of real visits meets each threshold, assessed separately for mobile and desktop.

Lab data and field data

Lab data comes from a controlled run: Lighthouse, the DevTools Performance panel, a CI job. It is repeatable, so it suits debugging and catching regressions. Field data comes from real users on real devices and networks, through the Chrome User Experience Report or your own real-user monitoring. Field data is what users experience; lab data explains why.

The two often disagree. A lab page load has no user interactions, so lab tools cannot report INP and use Total Blocking Time as a proxy. Collect field data with the web-vitals library:

import { onLCP, onINP, onCLS } from "web-vitals";

function send(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    page: location.pathname,
  });
  navigator.sendBeacon("/vitals", body);
}

onLCP(send);
onINP(send);
onCLS(send);

Improving LCP

LCP time breaks into server response, resource discovery, download and render. Work on whichever part is largest.

  • Make the LCP image discoverable in the HTML, not injected by JavaScript or hidden in a CSS background.
  • Never lazy-load it, and raise its priority.
  • Serve a correctly sized, compressed image with srcset and sizes.
  • Reduce server response time with caching and a CDN.
<img src="/hero-800.avif"
     srcset="/hero-800.avif 800w, /hero-1600.avif 1600w"
     sizes="100vw" width="1600" height="900"
     fetchpriority="high" alt="Team at work">

Fonts matter too: if the LCP element is text, a slow web font can delay it. Preload the one or two fonts used above the fold and use font-display: swap or optional.

Improving INP

Slow interactions almost always come from long tasks: JavaScript running for more than 50 ms on the main thread, during which the browser cannot respond or paint.

  • Ship less JavaScript. Split code by route and load rarely used features on demand.
  • Do the visible update first, then yield to the browser before non-urgent work such as analytics or saving drafts.
  • Avoid re-rendering large parts of the tree on every keystroke.
  • Watch third-party scripts; they share the same main thread.

Improving CLS

  • Give images and videos width and height attributes, or an aspect-ratio, so the browser reserves space.
  • Reserve space for ads, embeds and banners before they load.
  • Don’t insert content above what the user is reading; add it below or on user action.
  • Match fallback font metrics to the web font (with size-adjust and related descriptors) so the swap doesn’t reflow text.
  • Animate with transform, which does not shift layout.

Budgets and habits

A performance budget turns good intentions into a check that fails. Set limits on things you control, and enforce them in CI:

JavaScript per route    170 KB compressed
LCP image               200 KB
Lab LCP (mid mobile)    2.5 s
Third-party scripts     3
  • Watch field data for trends; use lab tools to find causes.
  • Test on a mid-range Android phone, not only your laptop.
  • Fix the biggest part of the biggest metric first.

Next: Fetching and caching data on the client

Loading and error states, race conditions, caching, optimistic updates and paginated lists.