PerformanceCore Web Vitals as requirements

100%

Core Web Vitals as requirements.

"The shop should feel fast" cannot fail a release, so it never stops one. Core Web Vitals give the wish three numbers, each about something a shopper notices: when the main content showed up, how long the page took to answer a tap, and how much the page jumped around. Add a percentile, a device class and a data source, and the wish becomes a line a team can be held to.

Beginner35 minUpdated 2 Oct 2026

Builds on How fast is fast enough.

The idea.

A shopper never sees your bundle size or your server time. They notice three things, and each one has a number.

Picture a rider on the Cobbleway shop with a squealing rear brake and a phone that is two years old. They open a product page for resin brake pads. Three questions run through their head without words. Has the thing I came for shown up yet? When I tapped the size, did the page react? Is the button still where I am about to press it? Every complaint about a slow site is one of those three in disguise.

Core Web Vitals give each question a metric measured inside the browser. Largest Contentful Paint (LCP) is the moment the biggest piece of content in the viewport finished drawing, a stand-in for "the page has arrived". Interaction to Next Paint (INP) is how long the page took to show a visible response after a click, tap or key press, taking close to the worst one in the visit. Cumulative Layout Shift (CLS) scores how much visible content moved without the user asking it to.

Each metric comes with two thresholds that split values into good, needs improvement and poor. The thresholds are not judged on one lucky test run. A page meets the bar when at least three out of four real page loads are good, measured separately for phones and for desktops. That last part is what turns a metric into a requirement: a number, a percentile, a device class and a data source, all of which can be checked by someone other than the person who wrote the code.

The three bars, all judged at the 75th percentile

LCP · loading
≤ 2.5 s
poor above 4 s
INP · responsiveness
≤ 200 ms
poor above 500 ms
CLS · visual stability
≤ 0.1
poor above 0.25 (a unitless score)
Assessed on
p75
of real page loads, phones and desktops separately

What each vital answers, and where its bands sit

VitalThe shopper's questionWhat the browser recordsGoodNeeds improvementPoor
LCPHas the main thing shown up?Render time of the largest image or text block in the viewport≤ 2.5 s2.5 s to 4 s> 4 s
INPDid the page answer me?Time from a click, tap or key press to the next frame drawn, near the worst of the visit≤ 200 ms200 ms to 500 ms> 500 ms
CLSDid it stay still?Largest burst of unexpected layout shift scores≤ 0.10.1 to 0.25> 0.25

Cobbleway on phones: each vital's worst page against its good line

Cobbleway on phones: each vital's worst page against its good lineEvery vital on Cobbleway's phones lands 36% to 70% past its good threshold, so a single fast lab run on a laptop would have hidden three failing requirements.050%100%150%200%LCP · product pageINP · category pageCLS · category page136%140%170%good thresholdVital (worst page template)Phone p75 as a share of the good threshold (%)Cobbleway on phones: each vital's worst page against its good lineEvery vital on Cobbleway's phones lands 36% to 70% past its good threshold, so a single fast lab run on a laptop would have hidden three failing requirements.050%100%150%200%LCP · product pageINP · category pa…INP · category pageCLS · category pa…CLS · category page136%140%170%good thresholdVital (worst page template)Phone p75 as a share of the good threshold (%)
Phone p75 over the last 28 days, as a share of each good threshold (100% is the line). LCP 3.4 s on the product page, INP 280 ms and CLS 0.17 on the category page. Illustrative Cobbleway numbers, derived in the section In practice.
Data
Vital (worst page template)% of threshold
LCP · product page136
INP · category page140
CLS · category page170
  • good threshold: Phone p75 as a share of the good threshold (%) = 100

From a wish to a line that can fail

VersionStatementWhat is missing
WishThe shop should load fastNo metric, no number: nobody can say it failed
MetricProduct page LCP under 2.5 sUnder 2.5 s for whom? One test run passes on a developer laptop
PercentileProduct page LCP ≤ 2.5 s at p75Mixing phones and desktops lets fast desktops hide slow phones
Device classProduct page LCP ≤ 2.5 s at p75 on phones, and on desktopsMeasured where? A lab run and real users disagree
RequirementProduct page LCP ≤ 2.5 s at p75 of field page loads (28 days), phones and desktops eachNothing; it can pass or fail
Was this section helpful?

How it works.

Each vital is a precise browser measurement with its own rules: which element counts, which interactions count, which shifts count. Knowing the rules is what lets you read a bad number correctly.

LCP, the largest thing that painted

While the page loads, the browser keeps a running answer to "what is the biggest piece of content on screen right now?". Each time something larger finishes painting it records a new candidate, and the last candidate before the user interacts is the LCP. Only some elements can be candidates: an img, an image inside an svg, a video (its poster or first presented frame, whichever comes first), an element whose background image comes from url(), and block-level elements that hold text.

Size means the part the user can see: whatever is off screen or clipped does not count, and neither do margins, padding or borders. Chromium also skips elements that are invisible (opacity 0), elements that cover the whole viewport (treated as background) and low-entropy placeholder images, so a blurry stand-in cannot claim the prize. Once the user taps, scrolls or presses a key, no new candidates are recorded; whatever was largest by then is the page's LCP.

How the LCP element changes while Cobbleway's product page paints

At about 1.2 s the HTML and CSS are in and the title block paints. The photo has not arrived, so the title is the largest content on screen and becomes the first candidate.

Text paints first. Store “Cobbleway”, showing the product view. Cart button “Basket” with badge 0 (its accessible name says “Cart, 0 items”, not the number alone). Product (showing) “Trailhawk resin disc pads (pair)” by “Fits 4-piston Trailhawk and Marlin calipers”: rating 4.7 (“146 reviews”); price £24.90. Gallery: image 1 of 4 (“Pads beside the caliper, top view”); thumbnails beside it. Compound (tiles, a radio group): Resin, Sintered. Buttons: “Add to basket”. Delivery: “Ships tomorrow from Leeds”. Folded sections: Fit guide, Reviews, Returns. Note on title: candidate 1: text block

  1. Usually the LCP element on a product page
  2. Text block; needs no download if the font is ready

Four parts that add up to LCP

An LCP time on its own says the page was slow, not why. Splitting it into four consecutive parts points at the culprit. Time to first byte runs from the start of the navigation until the first byte of HTML arrives. Resource load delay is the gap between that first byte and the moment the browser starts fetching the LCP resource, usually the hero image. Resource load duration is the download itself. Element render delay is the time from the download finishing until the element is actually on screen, often spent waiting for blocking scripts or styles.

When the LCP element is text in an already available font, there is nothing to fetch, so the two middle parts are zero. web.dev suggests a healthy page spends roughly 40% of its LCP on the first byte, roughly 40% on the download, and under 10% on each of the two delays. The delays are pure waiting, which is why they are the first place to look; how to remove them is the subject of the LCP breakdown topic.

Cobbleway's 3.4 s, part by part

Scenario 1 of 3: As described.

Timeline as a list

Cobbleway's 3.4 s, part by part: 5 lanes, from 0 ms to 4,000 ms.

  1. 0–3,400 ms · Product page load · LCP 3.4 s
  2. 0–700 ms · Time to first byte · 0.7 s
  3. 700–2,000 ms · Resource load delay · 1.3 s: waiting for the gallery script
  4. 2,000–2,900 ms · Resource load duration · 0.9 s photo download
  5. 2,500 ms · all lanes · good: 2.5 s (deadline)
  6. 2,900–3,400 ms · Element render delay · 0.5 s
  7. 3,400 ms · Element render delay · photo painted (error, violation)
A typical slow phone load of the product page (illustrative). The photo is named only in a script-rendered gallery, so the browser finds it 1.3 s after the HTML arrives. The 2.5 s rule falls in the middle of the photo download: the requirement was already lost to the 1.3 s wait before the fetch began.

Where the time goes, against the suggested shape

Cobbleway now
  1. TTFB 0.7 s, width 7, Time to first byte
  2. load delay 1.3 s, width 13, Load delay and render delay
  3. download 0.9 s, width 9, Resource load duration
  4. render 0.5 s, width 5, Load delay and render delay
  • photo requested only at 2.0 s, from 7 to 20
Suggested shape
  1. TTFB ~1.4 s, width 14, Time to first byte
  2. ~0.3, width 3, Load delay and render delay
  3. download ~1.4 s, width 14, Resource load duration
  4. ~0.3, width 3, Load delay and render delay
  • Time to first byte
  • Load delay and render delay
  • Resource load duration
Each cell is 100 ms of one 3.4 s load. The top row is Cobbleway's load above; the bottom row spreads the same 3.4 s in web.dev's suggested proportions (about 40% first byte, 40% download, under 10% for each delay). Cobbleway spends 1.8 s of its 3.4 s waiting.

INP, from a tap to the next frame

INP watches every click, tap and key press for the whole time the page is open, not only during loading. Hovering, scrolling and zooming are not counted. For each interaction the browser measures from the moment of input until the next frame is presented on screen, because that frame is the first chance the user has to see that something happened. The page's INP is the slowest of those interactions, with one allowance: for every 50 interactions, the single highest is ignored, so one hiccup in a long session does not define it. A visit with no counted interactions has no INP at all.

Each interaction's latency is three phases in a row. Input delay is how long the event waited before its handlers could start, usually because the main thread was busy with something else. Processing duration is the time spent in the event handlers themselves. Presentation delay is what comes after the handlers: recalculating styles, layout, paint and getting the frame to the screen. The three always add up to the interaction's latency, which is why INP is reported with them: each phase has a different owner and a different fix.

The filter tap on Cobbleway's category page

The rider taps Shimano fit at t = 0. A third-party reviews script is halfway through a long task, so the tap waits in the queue: the screen does not change.

Tap. Grid “Disc brake pads”, 2 columns: 4 items. Sort: Sort: Popular. Count: 64 products. Facet Brake fit (check): Shimano fit (23), SRAM fit (31) 1. Trailhawk resin pads · £24.90 [image 1:1] badges: Best seller. 2. Ridgeline sintered pads · £29.50 [image 1:1] 3. Mudlark resin pads · £18.00 [image 1:1] 4. Fellrunner semi-metallic pads · £32.00 [image 1:1] badges: New. Footer: a Load more button (“Show 60 more”). Note on facet:fit: tap at 0 ms · nothing drawn yet

  1. Filter options; each tap is one interaction
  2. Product grid re-rendered by the handler

One tap, three phases

Scenario 1 of 3: As described.

Timeline as a list

One tap, three phases: 3 lanes, from 0 ms to 400 ms.

  1. 0–70 ms · Main thread · reviews task
  2. 0 ms · Rider · tap
  3. 0–70 ms · all lanes · window: input delay 70
  4. 70–210 ms · Main thread · filter handler
  5. 70–210 ms · all lanes · window: processing 140
  6. 200 ms · all lanes · 200 ms (deadline)
  7. 210–280 ms · Main thread · style, layout, paint
  8. 210–280 ms · all lanes · window: presentation 70
  9. 280 ms · Screen · next frame (error, delayed)
The Shimano fit tap on a mid-range phone (illustrative). The rule at 200 ms is the good threshold measured from the tap. Input delay 70 + processing 140 + presentation 70 = 280 ms, in the needs-improvement band.

Nine interactions in one visit, and the one that becomes INP

Nine interactions in one visit, and the one that becomes INPEight of the rider's nine interactions are under 200 ms, yet the visit's INP is 280 ms because INP keeps the slowest one.050 ms100 ms150 ms200 ms250 msOpen menuPick categoryShimano fitSortOpen productResin tileAdd to basketType in searchClose basket64 ms120 ms280 ms150 ms96 ms48 ms170 ms40 ms56 msgoodInteraction, in orderLatency to next frame (ms)Nine interactions in one visit, and the one that becomes INPEight of the rider's nine interactions are under 200 ms, yet the visit's INP is 280 ms because INP keeps the slowest one.0100 ms200 msOpen menuPick categoryShimano fitSortOpen productResin tileAdd to basketType in searchClose basket64 ms120 ms280 ms150 ms96 ms48 ms170 ms40 ms56 msgoodInteraction, in orderLatency to next frame (ms)
One illustrative phone visit. With fewer than 50 interactions nothing is discarded, so the filter tap sets INP. In a visit of 120 interactions the two highest would be ignored.
Data
Interaction, in orderms
Open menu64
Pick category120
Shimano fit280
Sort150
Open product96
Resin tile48
Add to basket170
Type in search40
Close basket56
  • good: Latency to next frame (ms) = 200

The three phases and who usually owns them

PhaseFrom → toCobbleway tapUsual cause
Input delayinput → first handler starts70 msOther work on the main thread: third-party tags, hydration, timer callbacks
Processing durationfirst handler starts → last handler ends140 msThe page's own handlers: state updates, re-rendering a large list
Presentation delaylast handler ends → next frame shown70 msStyle, layout and paint of a big DOM change, or more work queued before the frame

CLS, the worst burst of movement

A layout shift happens when an element that was already visible starts a frame in a different position than it had in the frame before. The browser scores each shift by multiplying two fractions of the viewport: how much of the screen the moving elements touched across both frames (the impact fraction) and how far the farthest of them travelled, relative to the viewport's larger side (the distance fraction). On a portrait phone, cards that cover half the viewport and drop by a tenth of its height touch 60% of it across the two frames (their old and new positions together), so the shift scores 0.6 × 0.1 = 0.06.

Shifts are then grouped into session windows: a window keeps growing while each new shift comes less than 1 second after the previous one, and it closes after 5 seconds at most. CLS is the total of the worst window over the page's whole life, not the sum of everything, so a long-lived page is not punished just for being open. Shifts the user caused are left out: any shift within 500 ms of a tap, click or key press is flagged as having recent input and does not count, and animations done with CSS transform do not move layout at all. The causes and cures of each kind of shift are the subject of the Layout stability topic.

A late delivery strip on the category page

The grid is on screen at about 0.5 s, the web font swaps in at 0.6 s, and the rider reaches for the first card.

First paint. Grid “Disc brake pads”, 2 columns: 4 items. Sort: Sort: Popular. Count: 64 products. 1. Trailhawk resin pads · £24.90 [image 1:1] badges: Best seller. 2. Ridgeline sintered pads · £29.50 [image 1:1] 3. Mudlark resin pads · £18.00 [image 1:1] 4. Fellrunner semi-metallic pads · £32.00 [image 1:1] Footer: a Load more button (“Show 60 more”). Note on item:g1: the thumb is heading here

  1. Content inserted above what the rider sees
  2. Cards that move when it arrives

One visit's shifts, grouped into windows

Scenario 1 of 2: As described.

Timeline as a list

One visit's shifts, grouped into windows: 2 lanes, from 0 s to 8 s.

  1. 0.6 s · Layout shifts · 0.02
  2. 0.6–2.9 s · Layout shifts · window: window 1: 0.16
  3. 1.2 s · Layout shifts · 0.11 (error)
  4. 1.9 s · Layout shifts · 0.03
  5. 4.5 s · Layout shifts · recently viewed 0.04
  6. 4.5–5.5 s · Layout shifts · window: window 2
  7. 6 s · Rider · tap Show 60 more
  8. 6–6.5 s · all lanes · window: input
  9. 6.2 s · Layout shifts · 0.25 excluded (tick, allowed)
One illustrative phone visit to the category page; each mark is a layout shift and its score, and each shaded window runs until 1 s passes with no new shift. Window 1 holds three shifts less than 1 s apart: the web font swapping in, the delivery strip and the rating stars (0.02 + 0.11 + 0.03 = 0.16). Window 2 is a lone 0.04 from a recently viewed row. The big shift at 6.2 s comes 200 ms after the rider's tap, inside the 500 ms allowance, so it is excluded. CLS for the visit is 0.16.

The 75th percentile, per device class

Every page load produces one value per vital, and a busy page produces thousands a day. To turn that pile into a pass or a fail, the program takes the 75th percentile: sort the values and read off the one that three quarters of loads beat. If p75 LCP is 2.4 s, at least 75% of loads painted their main content within 2.4 s. The median would let half of all visits be slow; p95 or p99 would be dominated by a handful of terrible connections the site cannot fix. p75 sits between those, demanding that most visits are good while staying robust to outliers. The general maths of percentiles, and why you never average them, is in the backend topic on latency percentiles.

Phones and desktops are judged separately, against the same thresholds. The thresholds are the same because a person on a phone is no more patient than one at a desk; the assessment is split because mixing them hides the slow group. When most of a site's traffic is desktop, a blended p75 can pass while most phone loads are slow. In a requirement this means two lines, or one line that says "each".

Why the program reads p75

StatisticWhat it would let throughVerdict
MeanOne 30 s load on a dead connection drags it up; a few instant cached loads drag it downMoves with outliers, describes no real visit
p50 (median)Half of all loads may be slow and the page still passesToo lenient for a quality bar
p75A quarter of loads may miss the thresholdThe Core Web Vitals choice: most visits good, stable from day to day
p95 / p99Almost nothing, but the value is set by the worst networks and devicesUseful for diagnosis, too noisy for a pass mark

Field data, lab data and the supporting metrics

Field data comes from real visits: Chrome's public CrUX dataset (a 28-day rolling window, split by phone, desktop and tablet) or the site's own real-user monitoring. Lab data comes from a scripted load of one URL on one simulated device and network, as Lighthouse does. The thresholds were set for field data at p75, and that is what a Core Web Vitals requirement is judged on. A standard Lighthouse page-load run cannot even produce INP, because nobody taps during it; it reports Total Blocking Time as a stand-in. Lab runs are still essential, but for a different job: they are repeatable, so they catch a regression in a pull request long before 28 days of field data notice it. How the two are wired into a release process is covered in Lab data, field data and releases.

Supporting metrics, read as diagnostics, not as the bar

MetricWhereGoodWhat it tells you
TTFBfield and lab≤ 0.8 s (poor > 1.8 s)Server, CDN, redirects and connection setup. The first slice of every LCP; a rough guide, not a Core Web Vital
FCPfield and lab≤ 1.8 s (poor > 3.0 s)When anything at all painted. A big gap between FCP and LCP points at the hero resource; a late FCP at blocking CSS or scripts
TBTlab only< 200 ms on average mobile hardwareThe parts of long tasks beyond 50 ms, summed after FCP. A lab proxy for INP risk; not a substitute for it
Was this section helpful?

In practice.

Cobbleway turns its field numbers into requirement lines, and the same moves make a strong performance answer in a front-end system design interview.

Product page LCP on phones, last 28 days

Product page LCP on phones, last 28 daysOnly 44% of phone loads paint the product photo within 2.5 s, so p75 sits at 3.4 s and the page fails its LCP requirement even though the median is close to the line.05%10%15%20%750 ms1.25 s1.75 s2.25 s2.75 s3.25 s3.75 s4.25 s4.75 s5.25 sgood ≤ 2.5 sp50p75poor > 4 sShare of loads (%)LCP (s)Product page LCP on phones, last 28 daysOnly 44% of phone loads paint the product photo within 2.5 s, so p75 sits at 3.4 s and the page fails its LCP requirement even though the median is close to the line.05%10%15%20%75…750 ms1.…1.25 s1.…1.75 s2.…2.25 s2.…2.75 s3.…3.25 s3.…3.75 s4.…4.25 s4.…4.75 s5.…5.25 sgood ≤ 2.5 sp50p75poor > 4 sShare of loads (%)LCP (s)
Share of Cobbleway's phone product-page loads per half-second bin (illustrative). Bars sit at bin centres; the last bar holds every load above 5 s. 44% of loads are good, 43% need improvement and 13% are poor; p50 is about 2.7 s and p75 about 3.4 s.
Data
LCP (s)% of loads
750 ms3
1.25 s8
1.75 s14
2.25 s19
2.75 s14
3.25 s21
3.75 s8
4.25 s5
4.75 s3
5.25 s5
  • good ≤ 2.5 s: LCP (s) = 2.5
  • p50: LCP (s) = 2.7
  • p75: LCP (s) = 3.4
  • poor > 4 s: LCP (s) = 4

Cobbleway's p75 by page and device class

Page templatePhone LCPPhone INPPhone CLSDesktop LCPDesktop INPDesktop CLS
Home2.2 s150 ms0.051.5 s70 ms0.03
Category listing2.9 s ✕280 ms ✕0.17 ✕1.8 s120 ms0.08
Product page3.4 s ✕170 ms0.041.9 s90 ms0.02

Illustrative numbers, but a common shape. About seven in ten Cobbleway visits come from phones, and every failure is on a phone; the desktop columns pass everything. A team that tests on office laptops would see a healthy site, and one blended number per page would make the phone failures look smaller and would not say which device class to fix. Splitting by template matters as much as splitting by device: each template has its own LCP element, its own interactions and its own late content, so the fix for the product page's photo does nothing for the category page's filter chips.

No.AreaRequirementTarget / measure
01LoadingHome, category and product pages show their main content quickly on the rider's phone.
LCP ≤ 2.5 sp75phones and desktops each
Field data, 28-day window, per page template; reference phone and network as set in Designing for the device you don't own.
02ResponsivenessEvery click, tap and key press shows visible feedback within a perception-safe time.
INP ≤ 200 msp75phones and desktops each
Field data per template, with the slowest interaction targets listed so each one has an owner.
03Visual stabilityNothing the rider can see moves unless they asked it to.
CLS ≤ 0.1p75phones and desktops each
Field data; promotional strips and late widgets count against the page that hosts them.
04Diagnostics (watched, not gating)Server and first paint stay healthy enough to leave room for LCP.
TTFB ≤ 0.8 sFCP ≤ 1.8 sp75
An alert, not a release blocker; a breach explains an LCP regression.
05Pre-merge lab checkA pull request cannot merge if it regresses the lab run on the reference profile.
LCP ≤ 2.5 sTBT < 200 msCLS ≤ 0.1
Median of several runs; the byte and request limits behind it are set in Performance budgets.

Reporting each vital with its phases

import { onLCP, onINP, onCLS } from 'web-vitals/attribution';

function send(metric) {
  const a = metric.attribution;
  const row = {
    name: metric.name,          // 'LCP' | 'INP' | 'CLS'
    value: metric.value,        // ms for LCP and INP, a score for CLS
    rating: metric.rating,      // 'good' | 'needs-improvement' | 'poor'
    template: document.body.dataset.template, // e.g. 'product'
  };
  if (metric.name === 'LCP') Object.assign(row, {
    target: a.target, ttfb: a.timeToFirstByte, loadDelay: a.resourceLoadDelay,
    loadDuration: a.resourceLoadDuration, renderDelay: a.elementRenderDelay,
  });
  if (metric.name === 'INP') Object.assign(row, {
    target: a.interactionTarget, inputDelay: a.inputDelay,
    processing: a.processingDuration, presentation: a.presentationDelay,
  });
  if (metric.name === 'CLS') Object.assign(row, { target: a.largestShiftTarget });
  navigator.sendBeacon('/rum', JSON.stringify(row));
}

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

Saying it in an interview

Q1
RequirementsYou are listing what the product page must achieve beyond its features. How do you phrase speed?
Name the three vitals with their good thresholds and say how they are judged: LCP at most 2.5 s, INP at most 200 ms, CLS at most 0.1, at the 75th percentile of real page loads, phones and desktops separately. Then tie each to this page: the LCP element is the main product photo, the interactions that matter are the variant picker and Add to basket, and the shift risks are late price, stock and promotion content. That takes a minute and shows you know what will be measured.
Q2
Deep diveLater they ask how your design keeps LCP good. How do you use the vitals there?
Walk the four parts against your architecture. Server rendering or an edge cache keeps the first byte short; putting the photo in the initial HTML with a high fetch priority removes the load delay; a sized, compressed image shortens the download; not blocking the first render on the app bundle removes render delay. Each choice names the part it shrinks, and the details belong to the loading topics you link to.
Q3
Pushback"Our Lighthouse score is 98, so performance is done." How do you answer?
A lab score is one simulated load of one URL with no user input. It says nothing about INP, little about real phones on real networks, and nothing about the 75th percentile. Keep the lab check as a regression gate in CI, and judge the requirement on field data split by device class and page template.
Q4
Trade-offProduct wants a promotional banner on every category page. What do you ask for?
That its space is reserved in the server's HTML so its arrival moves nothing, that it is counted inside the page's CLS and LCP requirements rather than exempted, and that its script loads after the main content so it cannot delay the first interaction.

Where the vitals show up in the system flows

Product detail page
LCP is the main gallery image; INP is the variant picker and add to cart; CLS comes from price, stock and delivery estimates that arrive after the shell. See the Amazon-like product detail page.
Listing page with a photo gallery
The first gallery photo is the LCP element on both phone and desktop, and the server-rendered page is what keeps the first byte and the load delay short. See the Airbnb-like listing page.
News feed
INP comes from reactions, comments and opening menus during long sessions; CLS from posts, ads and "new posts" content inserted above the reader. See the Facebook-like news feed.
Infinite photo feed
Images without reserved space are the classic CLS source, and long sessions with many taps make INP's one-in-50 allowance matter. See the Instagram-like infinite photo feed.
Was this section helpful?

Trade-offs.

The vitals are good proxies, not the whole truth. Choosing what to judge on, knowing where the numbers disagree and what they cannot see keeps a team honest.

01
What a release is judged against
Chosen:Field p75 per device class and page template, with a lab check in CI
  • Pro:Matches how the thresholds were designed
  • Pro:Real devices and networks
  • Pro:INP is actually measured
  • Pro:Lab check still stops regressions before merge
Downside we accept:
  • Con:Field data lags by days to weeks
  • Con:Needs enough traffic per template to be stable
  • Con:Two systems to run
Ruled out:A lab performance score only

No INP; One simulated device and network; Easy to pass while real phones fail

Ruled out:Field p95 instead of p75

Dominated by networks and devices the site cannot fix; Noisy from week to week; Not comparable with CrUX or other sites

02
Where the field numbers come from
Chosen:Own real-user monitoring, cross-checked against CrUX
  • Pro:Every browser that supports the APIs
  • Pro:Split by template and release
  • Pro:Phase attribution for each slow load or tap
Downside we accept:
  • Con:Instrumentation and a pipeline to build
  • Con:Sampling and consent to handle
Ruled out:CrUX only

Eligible Chrome users only; 28-day rolling window; No attribution; Low-traffic pages may have no data

Why the lab and the field disagree

DifferenceIn the labIn the field
Device and networkOne simulated profile, picked by the teamEvery phone and connection your visitors have
Cache stateUsually a cold loadA mix of first visits, repeat visits and back/forward restores
InteractionsNone; TBT stands in for INPReal clicks and taps for the whole visit
Page lifetimeStops soon after load, so late shifts are missedUntil the tab is hidden or closed
ContentOne URL, often a logged-out viewPersonalised pages, consent states, A/B variants, ads

What the vitals do not see

Blind spotWhy it mattersWhat to add
Soft navigations in single-page appsA route change inside an SPA is not a new page load, so LCP describes only the first view. Chrome 151 shipped the Soft Navigations API, but how CrUX will report soft navigations is not settledYour own timing per route, for example from the moment of the click to the new view's main content
Visitors outside CrUXCrUX counts eligible Chrome users only: no Safari, Firefox or Chrome on iOS. LCP and INP have been measurable in every major engine since December 2025; CLS was still Chromium-only thenRUM across browsers, reported per browser family
CorrectnessA fast page that shows the wrong price or fails to add to the basket passes every vitalError rates and task success next to the vitals
Smoothness after the tapINP stops at the next frame; a janky scroll or a stuttering carousel is not an interactionFrame-rate checks for animations
Cost to the deviceBattery, data and memory use are invisible to all threeByte and CPU budgets, tested on the reference device
Every interaction's own targetINP is one number per visit; a slow but rare action can hide behind itA per-interaction target (Response time limits) tracked by interaction target
Not used:Drawing the hero photo into a canvas, which is not an LCP candidate, so the title text becomes the LCP elementThe number improves while the shopper still waits for the photo they came to see. Requirements should name the intended LCP element per template.
Not used:Showing a tiny blurred placeholder in the hero slotChromium ignores low-entropy placeholders as LCP candidates, so this mostly does not even move the number; a real, smaller image does.
Not used:Exempting the promotional strip from CLS because marketing owns itThe browser does not know who owns a script. Shared components count against the page that hosts them.
Not used:Loading third-party tags only after the first interactionThat first interaction then pays their cost as input delay, which is exactly what INP measures.
Used:Fixing the cause the attribution points at, then checking the field p75 a few weeks laterSlow, but the only change that helps the shopper and the number together.
This topic
What LCP, INP and CLS measure and their thresholds
The INP phases and LCP subparts as definitions
p75 per device class, and field versus lab as the basis of a requirement
Writing the requirement lines
Elsewhere
Removing LCP delays and shrinking the heroPage load time, LCP breakdown
Yielding, splitting long tasks, debouncing handlersJavaScript performance, Long tasks and INP
Causes of layout shifts and reserving spacePage load time, Layout stability
Collecting field data and comparing releasesFrontend monitoring
Choosing the reference device and turning targets into byte budgetsTarget devices and networks; Performance budgets
Was this section helpful?
Builds on this
Designing for the device you don't own
Read next