# Core Web Vitals

> LCP, INP and CLS: loading, responsiveness and visual stability, measured on real users at the 75th percentile.

Concept · Building apps · https://levelcode.org/concepts/core-web-vitals

**Core Web Vitals** are three metrics Google uses to describe how a page *feels* to the people using it. They're measured in real users' browsers, and a page passes when it hits the target for at least 75% of visits.

| Metric | Measures | Good | Poor |
|---|---|---|---|
| **LCP** — Largest Contentful Paint | When the main content (biggest image or text block) appears | ≤ 2.5 s | > 4 s |
| **INP** — Interaction to Next Paint | Delay from a click, tap or key press to the next frame | ≤ 200 ms | > 500 ms |
| **CLS** — Cumulative Layout Shift | How much visible content jumps around unexpectedly | ≤ 0.1 | > 0.25 |

## LCP: show the main thing sooner

LCP time breaks into four parts: server response (TTFB), the delay before the browser *discovers* the resource, the download, and the render. Common fixes:

- Make the hero image discoverable in the HTML. Don't lazy-load it, and add `fetchpriority="high"`.
- Serve modern, correctly sized images (AVIF/WebP with `srcset`).
- Cut render-blocking CSS and JS on the [critical path](https://levelcode.org/concepts/critical-rendering-path).
- Use server rendering or a [CDN](https://levelcode.org/concepts/cdn) for a fast first byte. See [rendering strategies](https://levelcode.org/concepts/rendering-strategies).

## INP: keep the main thread free

An interaction can't produce a frame while a [long task](https://levelcode.org/concepts/event-loop) is running. INP reports roughly the slowest interaction on the visit, so one heavy click handler is enough to fail it.

- Break up long tasks, and yield to the browser with `scheduler.yield()` or `await new Promise(r => setTimeout(r))`.
- Do the visible update first and defer the rest, using `startTransition` in React.
- Ship less JavaScript and do less hydration. See [code splitting](https://levelcode.org/concepts/bundling-code-splitting).

## CLS: reserve space

- Set `width`/`height` or `aspect-ratio` on images, videos and embeds.
- Reserve space for ads, banners and late-loading content instead of inserting it above what the user is reading.
- Use `font-display: optional` or matched fallback metrics so web fonts don't reflow text.
- Animate with `transform`, not `top` or `height`.

## Lab data vs field data

Lighthouse is **lab** data: one simulated load on one simulated device. It's good for debugging but not the score that counts. **Field** data from real users, collected by the Chrome UX Report or your own RUM with the `web-vitals` library, is what's assessed. Because the threshold is the 75th percentile, you're tuning for slow phones and slow networks, much like the server-side lesson that [tail latency](https://levelcode.org/concepts/tail-latency) matters more than the average.
