skip to content

Core Web Vitals

Core Web Vitals are the three user-centric metrics search ranking and most perf reviews are built on, so nearly every performance interview starts here. You should be able to say what each one measures, what typically breaks it, and how it is scored.

on this pageshow

explore

questions

23

What does the Cumulative Layout Shift (CLS) metric measure on a web page, and which kinds of on-screen movement does it deliberately not count?

level: juniorimportance: must knowfreq 74%

answer

  1. unexpected movement, not all movement
  2. a score, not seconds or pixels
  3. recent click buys a short exemption
  4. transform moves cost nothing
  5. new element pushes, does not shift

basics

~20 s

CLS scores how much visible content moves unexpectedly during a page's life. It ignores movement the user just triggered — shifts within 500 milliseconds of a click, tap or key press — and elements moved with CSS transforms.

solid answer

~50 s

CLS is the Core Web Vital for visual stability. It looks for *unstable elements*: elements that were visible in one rendered frame and start at a different position in the next one, without the user having asked for that. Each such movement gets a small unitless score based on how much of the viewport was affected and how far things moved, and CLS reports the worst burst of those scores over the page's lifetime. Two big carve-outs keep it honest. Movement within 500 ms of a discrete user input — a tap, click, or key press — is flagged `hadRecentInput` and excluded, because opening an accordion is expected. And elements animated with CSS `transform` are not layout shifts at all, since their layout position never changed. An element merely appearing, or growing with nothing below it, also scores nothing until it pushes other content.

go deeper

for a junior

Be able to say plainly that CLS is about content jumping around, that it is a unitless score rather than a time, and that movement right after the user clicks something is not counted.

for a middle

Explain the definition mechanically: an already-visible element whose start position changes between two frames, why an element that merely appears scores nothing for itself, and why transform animations are free while animating height is not.

for a senior

An interviewer expects you to go from a bad score to a named cause. Talk about reading layout-shift entries and their sources, and about the fact that the metric keeps accruing for the whole document lifetime, not just the load.

for a principal

Own the framing: visual stability is a design and delivery constraint, not a bug to be fixed late. Be ready to argue for rules — reserved space for anything asynchronous, no injection above existing content — enforced at the component and template level rather than per incident.

## The problem the metric exists for Every user has hit this: you go to tap a link, an ad or a banner loads above it, the page jumps, and you tap something else. Nothing about that experience shows up in a loading metric — the page may have painted quickly and responded to input quickly. CLS is the Core Web Vital that puts a number on this specific failure: **visible content moving when the user did not ask for it**. ## What the browser actually watches The browser compares consecutive rendered frames. An element is an **unstable element** for a frame if it was visible in the previous frame and its start position — the top-left of its box in the viewport, in the writing direction — is different in the current frame. Note the three conditions hiding in that sentence: - **Visible.** Content scrolled far out of the viewport is not part of the shift, because the user could not have been reading it. - **Was there before.** An element that did not exist in the previous frame has no previous position, so it cannot be an unstable element. Injected content does not score for *itself*; it scores because of the elements it **pushes**. - **Start position changed.** An element that only grows or shrinks, with nothing below it to displace, is not shifting. A card that gets taller at the bottom of the page moves nothing. Each shift gets a small unitless score derived from how much of the viewport was involved and how far the content travelled, and those scores accumulate over the page's life. ## What does not count Three exclusions come up constantly in interviews. **User-initiated movement.** A shift that occurs within 500 ms of a discrete user input — tap, click, key press — is exempt. The `layout-shift` performance entry carries a `hadRecentInput` boolean for exactly this, and shifts with it set contribute nothing. Expanding a menu, opening an accordion, or toggling a filter is movement the user requested and is watching for. **Transform-based movement.** If you move an element with `transform: translateY(...)`, its layout position never changed — the browser only draws it somewhere else. That is not a layout shift, which is why a slide-in animation built on `transform` costs nothing while the same animation built by animating `height` or `margin` costs plenty. This is the single most useful practical consequence of the definition. **Off-screen and never-visible content.** Shifts of content that was not visible in the viewport do not count. ## What CLS is not It is not a duration — there are no seconds in it — and it is not a pixel count. It is a **unitless score**, which is why the guidance is expressed as a bare number rather than a time budget. It is also not an initial-load-only metric: it keeps accruing for as long as the document is alive, so a shift triggered ten minutes into a session by a late-loading widget is still eligible. ## Where the shifts usually come from In practice a handful of patterns account for nearly all real CLS: - content injected **above** existing content after first paint — consent bars, promo strips, notification banners, A/B-test variants; - media and embeds whose box the browser could not know in advance, so the surrounding layout is laid out once without them and once with them; - content whose height changes when data arrives — a skeleton row replaced by a taller real row; - late style or webfont application that changes the size of text blocks. All of them are the same underlying mistake: the first layout the user saw was computed with information that later turned out to be wrong. ## Seeing it yourself Every individual shift is exposed to JavaScript as a `layout-shift` entry through `PerformanceObserver`, with a `value` (that shift's score), a `hadRecentInput` flag, and a `sources` array naming the nodes that moved and their before/after rectangles. That is the fastest way to move from "CLS is bad" to "this element moved 80 px at t = 2.1 s". ```js new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (!entry.hadRecentInput) console.log(entry.value, entry.sources); } }).observe({ type: 'layout-shift', buffered: true }); ``` ## The one-sentence version CLS answers "did the page move under the user's fingers?" — counting only movement of already-visible content that the user did not initiate, and only when the element's real layout position changed.

  • A spinner is replaced by content of exactly the same size. Does that count as a layout shift?
    No. The spinner disappearing and the content appearing are not shifts in themselves — neither element had a previous position that changed — and because the boxes are the same size, nothing around them starts at a new position either. That is precisely why a well-sized skeleton placeholder is a CLS fix: the layout the user first sees is already the final one.
  • Why is CLS reported as a bare number with no unit?
    Because each shift's score is the product of two viewport-relative fractions: how much of the viewport was affected and how far content moved as a share of the viewport's larger dimension. Both are ratios, so the product is unitless, and scores are comparable between a phone and a desktop monitor rather than being dominated by screen size.
  • Does a shift that happens while the user is scrolling get the user-input exemption?
    No. The exemption is for discrete inputs — tap, click, key press — within 500 ms. Continuous interaction like scrolling is not treated as recent input, which is deliberate: content that loads in as you scroll and shoves the page around is exactly the experience CLS is meant to catch.

saying these in an interview costs you the question

  • Says CLS is measured in seconds or milliseconds
  • Claims any animation on the page hurts CLS
  • Believes CLS only covers the initial page load
  • Thinks an element appearing always counts as a shift
  • Assumes shifts right after a user click still count

context

open as a page

On a web page, which user interactions does the Interaction to Next Paint (INP) metric measure, and which common ones does it ignore?

level: juniorimportance: must knowfreq 70%

basics

~20 s

INP measures discrete interactions — clicks, taps and key presses — timing each from the user's input to the next frame painted after the page responds. Scrolling and hovering are excluded, and one value per page visit is reported.

open as a page

What does the Largest Contentful Paint (LCP) metric measure on a web page, and which elements are eligible to be reported as the LCP element?

level: juniorimportance: must knowfreq 85%

basics

~20 s

LCP records when the largest content element visible in the viewport finished rendering, timed from the start of navigation. Eligible candidates are images, SVG <image> elements, video poster frames, elements with a CSS background-image, and block-level elements containing text.

open as a page

In web performance, what does First Contentful Paint (FCP) mark on a page load, what counts as "contentful" for it, and what does a good FCP still fail to tell you about the experience?

level: juniorimportance: must knowfreq 66%

basics

~20 s

First Contentful Paint marks the moment the browser first renders any piece of DOM content — text, an image, a non-white canvas or an SVG. It proves the page has stopped being blank, but says nothing about whether the main content or anything usable has arrived.

open as a page

How is the score for a single layout shift calculated, and how are the individual shifts on a page combined into the reported Cumulative Layout Shift (CLS) value?

level: middleimportance: must knowfreq 56%

basics

~20 s

Each shift scores impact fraction times distance fraction: the share of the viewport the moving content covered before and after, times how far it moved relative to the viewport's larger side. The page reports the largest sum inside one session window.

open as a page

INP breaks a single interaction's latency into input delay, processing time, and presentation delay. What happens during each phase, and what typically makes each one slow?

level: middleimportance: must knowfreq 80%

basics

~20 s

Input delay is the wait before any handler runs, usually because the main thread is busy. Processing time is the event handlers themselves. Presentation delay is the styling, layout and paint work needed before the next frame shows the result.

open as a page

Core Web Vitals replaced First Input Delay (FID) with Interaction to Next Paint (INP) in 2024. What did FID measure, and why could a page with a good FID score still have poor INP?

level: middleimportance: must knowfreq 68%

basics

~20 s

FID timed only the queueing delay before the first interaction's handler started. INP measures every qualifying interaction all the way to the next paint, so slow handlers, heavy rendering, and interactions after the first one — all invisible to FID — now count.

open as a page

A page's Largest Contentful Paint (LCP) comes in at 4.2 seconds. How do you break that single number into its component phases to find where the time actually goes?

level: middleimportance: must knowfreq 65%

basics

~20 s

Split LCP into four consecutive phases: time to first byte, resource load delay (before the LCP resource starts downloading), resource load duration, and element render delay. Each phase points at a different cause, and a text LCP has both resource phases at zero.

open as a page

In web performance, what does Time to First Byte (TTFB) measure for a page navigation, which phases of the request are folded into that single number, and why does a high TTFB drag the paint metrics with it?

level: middleimportance: must knowfreq 62%

basics

~20 s

TTFB measures the time from the start of a navigation until the first byte of the main document's response arrives. It folds in redirects, DNS, connection setup, TLS and server processing, so every paint metric can only begin after it.

open as a page

Core Web Vitals are reported from real-user field data. What does it actually take for a URL to "pass" the Core Web Vitals assessment?

level: middleimportance: must knowfreq 72%

basics

~20 s

A URL passes only when every Core Web Vital — LCP, INP and CLS — sits in the good band at the 75th percentile of real visits, evaluated separately for mobile and desktop over a trailing 28-day window of field data.

open as a page

A Core Web Vitals field report shows each metric as three bars labelled good, needs improvement and poor. What is being counted in those bars, and where do the band boundaries come from?

level: juniorimportance: should knowfreq 46%

basics

~20 s

Each Core Web Vital has two fixed boundaries that split its values into good, needs improvement and poor. Every real visit produces one value that lands in one band, and the bars show the share of visits in each band.

open as a page

During a page load the browser emits several Largest Contentful Paint candidates. When does it stop updating the LCP value, and how can that make a slow page's field LCP look better than the experience actually feels?

level: middleimportance: should knowfreq 45%

basics

~20 s

The browser reports a new LCP candidate each time something larger paints and stops looking once the user first interacts — a tap, click, scroll or key press — or once the page is backgrounded. An impatient user who scrolls early freezes the value at whatever had painted by then.

open as a page

What does the Total Blocking Time (TBT) metric measure about a page load, how is its value computed, and why do lab tools use it as a stand-in for a field responsiveness metric they cannot measure?

level: middleimportance: should knowfreq 48%

basics

~20 s

Total Blocking Time sums the blocking portion of every long task during load — the milliseconds each task runs beyond 50ms. It estimates how unresponsive the page would have been to input, which lab tools use as a proxy because no synthetic run has real user interactions to measure.

open as a page

You shipped a change that clearly cut LCP, but a week later the Core Web Vitals field data for that page has barely moved. Why, and what would you look at in the meantime?

level: middleimportance: should knowfreq 48%

basics

~20 s

Field Core Web Vitals are aggregated over a rolling 28-day window, so a week after a deploy roughly three quarters of the samples still come from the old code. The reported value drifts as the window rolls; your own real-user data shows the change the same day.

open as a page

A page paints quickly, then a cookie-consent bar and a promo strip are injected at the top of the document a moment later, pushing the article down; the page's CLS is poor. Why does this pattern score so badly, and what would you change without removing either banner?

level: seniorimportance: should knowfreq 54%

basics

~20 s

Injecting content above existing content moves nearly everything on screen, so the impact fraction approaches 1, and two injections seconds apart land in the same session window and add. Fix it by rendering the bars in the initial HTML, or taking them out of document flow.

open as a page

In a single-page app users stay on one document for many minutes and change views client-side, and the app's CLS is poor even though the initial load produces almost no shifts. How does CLS accumulate across a session like that, and how would you attribute a shift to the view that caused it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

CLS keeps accruing for the whole document lifetime and is not reset by a client-side route change, so any view transition can define the score. Attribute shifts by logging each layout-shift entry with its value, timestamp, source nodes and the current route.

open as a page

Field data shows a page's INP around 450 ms, but the click handler for the slow control measures about 8 ms of its own JavaScript. Where is the remaining latency likely to be, and how would you confirm it?

level: seniorimportance: should knowfreq 52%

basics

~20 s

A fast handler only rules out the processing phase. The remaining time is either input delay — the main thread was busy when the click arrived — or presentation delay, the style, layout and paint work the handler's DOM changes triggered. Capture the phase breakdown from real users to tell which.

open as a page

A page visit contains many clicks and key presses, but INP reports one number. How is that value chosen from all the interactions, and why can a single very slow interaction in a long session fail to appear in it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

INP reports the slowest interaction of the visit, not an average — but on interaction-heavy pages it discards roughly one outlier per fifty interactions, so on a long session a single freak stall can be the discarded one and never surface.

open as a page

Two pages both report a Largest Contentful Paint (LCP) of about 4 seconds. On the first, the phase breakdown is dominated by resource load delay; on the second, by element render delay. What is likely wrong with each, and how does the remedy differ?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Dominant load delay means the browser found out about the LCP resource too late — it was not in the initial HTML, or was deprioritised. Dominant render delay means the bytes arrived but painting was blocked, by render-blocking resources, a busy main thread, or content that only exists after client-side rendering.

open as a page

Two pages both report a field LCP of about 4 seconds. On page A, TTFB is 2.6s and FCP is 2.9s. On page B, TTFB is 0.2s and FCP is 0.5s. What does each profile tell you about where the time is going, and how do the investigations differ?

level: seniorimportance: should knowfreq 52%

basics

~20 s

On page A almost all of the 4 seconds is spent before anything renders, so the problem is the server or the network and no front-end change will help. On page B the browser painted in half a second and then waited 3.5 seconds, so the largest element itself is the problem.

open as a page

A page scores well when you run a one-off performance test on your laptop, yet its Core Web Vitals field assessment fails. Give the reasons both results can be honest.

level: seniorimportance: should knowfreq 58%

basics

~20 s

A single test measures one device, one network, one location and one page state; the field assessment reads the 75th percentile across every real visit, including slow phones and bad networks. Some vitals also cannot be produced by a load-only test at all.

open as a page

A performance report shows Core Web Vitals field data for one of your pages but labels it origin-level, with no URL-level record available. Why does that happen, and how should you read that data when deciding what to fix?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Field datasets publish a URL-level record only when that URL has enough qualifying visits in the window; low-traffic pages get none and fall back to the origin aggregate, which is traffic-weighted across every page on the site and therefore describes your busiest templates, not this page.

open as a page

In web performance, what does the Speed Index metric measure about a page load, how does it differ from a milestone metric like First Contentful Paint, and when does it disagree with them?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

Speed Index scores how quickly the visible viewport fills in during load, computed from frame-by-frame visual progress rather than from a single event. Unlike milestone metrics it rewards steady progressive rendering and penalizes a page that stays blank and then appears all at once.

open as a page