skip to content

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%

answer

  1. biggest visible thing, not the whole page
  2. viewport-clipped, decoration excluded
  3. short eligibility list, images and text blocks
  4. intrinsic size caps an upscaled image
  5. candidates keep updating until measurement stops

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.

solid answer

~50 s

LCP reports the render time of the largest content element visible inside the viewport, measured from the start of the navigation. It is the cheapest useful proxy for "the main content is on screen". The candidate set is deliberately small: `<img>` elements, `<image>` inside an `<svg>`, a `<video>` element's poster frame, any element carrying a CSS `background-image` loaded with `url()`, and block-level elements that contain text. Size is the area actually visible in the viewport — parts that overflow are clipped, margins, padding and borders don't count, and for an image the size is capped at its intrinsic size, so blowing a thumbnail up to full width does not make it the largest candidate. Fully transparent elements and images the browser treats as page backgrounds are ignored. The browser emits a new candidate every time something larger paints, so the value only settles when measurement stops. Under 2.5 seconds is the "good" bar.

code

javascript · 10 lines
javascript
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('LCP candidate', {
      time: Math.round(entry.startTime),
      size: entry.size,
      url: entry.url || '(text)',
      element: entry.element,
    });
  }
}).observe({ type: 'largest-contentful-paint', buffered: true });

go deeper

for a junior

Be able to say plainly that LCP is the render time of the biggest content element visible in the viewport, and name the candidate types: images, video posters, CSS background images, and blocks of text.

for a middle

Explain the sizing rules — viewport clipping, no margins or padding, and the intrinsic-size cap on images — and why the browser emits a stream of candidates rather than one final value.

for a senior

Show that you read the reported element first, not the number: a text LCP and an image LCP have entirely different failure modes, and a suspiciously good LCP often means the real hero never painted at all.

for a principal

Own the framing question of whether the largest element is genuinely the element that matters for your product, and be ready to say when LCP is a poor proxy for perceived readiness and what you would track alongside it.

## What the metric is for Largest Contentful Paint answers one question: *when did the page start looking loaded to this user?* Earlier attempts at that question used First Contentful Paint (the first pixel of any content) or the `load` event, and both lie in opposite directions. FCP fires when a spinner or a logo paints, long before anything useful is on screen. `load` fires after every tracking pixel and offscreen image has finished, long after the user has already started reading. LCP splits the difference with a cheap heuristic: the *biggest* piece of content in the viewport is usually the piece the page is about — the hero image, the product photo, the article's opening paragraph — so the moment it renders is a decent stand-in for "loaded". The reported value is a timestamp relative to the start of the navigation, not a duration of anything. "Good" is 2.5 seconds or less; the poor boundary is 4 seconds. ## Which elements can be candidates Only a short list of element types qualifies: - `<img>` elements, - `<image>` elements inside an `<svg>`, - `<video>` elements (the poster image; browsers have since extended this to the first painted frame), - any element whose CSS `background-image` is loaded from a `url()` — gradients do not count, because nothing is fetched, - block-level elements containing text nodes or inline text children. Notice what is missing. Gradients, borders, box-shadows, canvas drawing and pseudo-element decoration are not content. Neither is an element that merely *contains* a big image — the `<img>` itself is the candidate, not its wrapper `<div>`. ## How "largest" is computed Size means visible area in the viewport, and three rules trim it: 1. **Clipping.** Only the part inside the viewport counts. A hero that runs off the bottom of the screen is measured at its on-screen rectangle, and content that only exists below the fold is not a candidate at all. 2. **No box decoration.** Margins, padding and borders are excluded. For text, the measured rectangle is the smallest box covering the text nodes. 3. **Intrinsic-size cap for images.** The size used is the smaller of the displayed size and the image's own intrinsic size. A 200×150 thumbnail stretched across a 1200px viewport is scored as 200×150. This exists so that a stretched placeholder cannot masquerade as the page's main content. Some elements are excluded outright: elements with `opacity: 0` (invisible to the user, so they have not "rendered" for LCP purposes), images that span the whole viewport and are effectively page backgrounds, and very low-entropy placeholder images such as a flat solid-colour block. ## The candidate is provisional During load the browser reports a *sequence* of candidates. The first thing that paints becomes the current largest; when something bigger paints, a new entry is emitted. The last entry before measurement ends is the page's LCP. That is why an LCP value read halfway through load means nothing, and why the reported element can be a heading on one load and the hero image on another. This also produces one of the most common surprises in the field: if the hero image never loads, it never paints, so it never becomes a candidate, and the page happily reports a fast LCP for the headline text while the user is staring at an empty box. ## Reading it in the browser The metric is exposed as `largest-contentful-paint` performance entries. Each entry carries `startTime` (the render timestamp), `size`, `element` (the DOM node), `url` (for image candidates), plus `loadTime` and `renderTime`. ```js new PerformanceObserver((list) => { const last = list.getEntries().at(-1); console.log(last.startTime, last.size, last.element); }).observe({ type: 'largest-contentful-paint', buffered: true }); ``` `buffered: true` matters: candidates fire early, often before your script runs, and without it you miss them. ## What a bad LCP usually means Because the metric is a timestamp of one element rendering, a bad number always decomposes into: the server was slow to answer, the element's resource was discovered late, the resource took a long time to download, or the element was ready but something stopped it painting. Knowing which element was chosen is therefore the first diagnostic step — a text LCP and an image LCP fail for completely different reasons, and treating them the same is how teams end up compressing an image that was never the problem.

  • If the element that should have been the largest fails to load, what does the page report as its LCP?
    A resource that never paints is never a candidate, so LCP falls through to the next-largest thing that did paint — usually a heading or a paragraph. The page then reports a fast LCP while the user looks at an empty hero slot, which is exactly the case where the metric flatters a broken experience.
  • Does content below the fold ever contribute to LCP?
    No. Only the area visible in the viewport counts, and an element that extends past the edge is measured at its clipped on-screen rectangle. Something the user must scroll to reach is not a candidate, and in practice scrolling also ends measurement, so it could not become one afterwards either.
  • Why can fading the hero in with a CSS transition make LCP worse?
    An element at `opacity: 0` is not visible, so it does not count as rendered. The candidate is only registered once it actually paints visibly, which means the fade's duration is added to the timestamp. Animating the hero in is one of the few purely cosmetic choices that directly moves a Core Web Vital.

It is like judging when a newspaper page is "printed" by watching the single biggest photo or headline block appear, rather than waiting for every last footnote and advert to arrive.

saying these in an interview costs you the question

  • Says LCP measures when the whole page has finished loading
  • Thinks the largest element anywhere in the document counts, not just the viewport
  • Believes only images can ever be the LCP element
  • Claims an upscaled thumbnail counts at its displayed size
  • Confuses LCP with First Contentful Paint

context