# Web vitals


Core Web Vitals are five numbers the browser measures about a page load: how fast it answered, how fast it painted, how fast it reacted to a click, and how much it moved around on the way. Flare collects all five and shows them per page.

When you turn on tracing, the Flare SDK automatically collects them for you.

## The five vitals

| Vital | What it measures | Unit |
| --- | --- | --- |
| TTFB | Time to first byte. How long it took before the server started answering. | milliseconds |
| FCP | First contentful paint. When the first text or image showed up. | milliseconds |
| LCP | Largest contentful paint. When the biggest element on screen finished painting. | milliseconds |
| INP | Interaction to next paint. The slowest reaction to a click, tap, or key press during the whole visit. | milliseconds |
| CLS | Cumulative layout shift. How much the page moved around while it loaded. | none, it's a decimal (0 = very good, 1 = very bad) |

## Turning them on

Web vitals come with tracing. Set `enableTracing` and our client will automatically measure them:

```js
import { flare } from '@flareapp/js';

flare.configure({
    enableTracing: true,
    tracesSampleRate: 1,
});
```

## Only the first page load gets vitals

The browser produces one set of these five numbers per document. A document is what you get when someone types your URL, follows a link from another site, or hits refresh.

In a single page app, a client side route change is not a new document. Your router swaps out what is on screen, but the browser is still on the page it loaded at the start, so there is no second set of numbers for it to measure.

That is why a `browser_navigation` span never carries vitals. There is one set of measurements per page, and it belongs to the load that started the visit.

## I'm missing some vitals?

If you open a trace right after a page load and you only see TTFB and FCP, that's normal. The other three are still being measured (LCP, CLS and INP).

The client sends them at the first client side route change, or when the visitor leaves, whichever comes first. Reload the trace after a few seconds and all five should be there.

The JS client sends them once and never updates them. On a normal site that means they cover the whole visit. In single page applications this only covers the initial pageload, so any navigation after that, does not record web vitals. This is by design, because clientside routing does not suffer from TTFB, FCP or LCP.

## Sampling

Vitals follow the same sampling decision as the trace they belong to. Set `tracesSampleRate` to `0.2` and roughly 1 in 5 page loads report vitals, along with everything else in that trace. A page load that was sampled out reports none.

There is no way to sample vitals separately. A vital with no page view to attach it to has nothing to describe. See [sampling](/docs/javascript/tracing/sampling) for the full picture.

## A vital your browser can't measure is left out

Not every browser measures every vital. When yours doesn't, Flare receives no value for it rather than a zero. A zero is a real measurement, and a page with no layout shift at all really does score `0` for CLS, so sending a zero for something that was never measured would be a lie.

Browser support, checked in August 2026:

- TTFB, FCP, LCP, and INP work in Chromium browsers, Firefox, and Safari.
- CLS only works in Chromium browsers.

So a visit in Firefox or Safari normally reports four vitals, and a visit in Chrome or Edge reports five. The client uses [Google's `web-vitals` library](https://github.com/GoogleChrome/web-vitals) to do the measuring, so support follows whatever that library supports.

## Reading the numbers

The values land on the browser pages table and in the trace viewer. See [web vitals in Flare](/docs/flare/performance/web-vitals) for what the columns mean, where the thresholds sit, and how the score is worked out.
