When the biggest thing in the viewport finished rendering. Diagnosing it means splitting it into four phases and finding which one is long — because the fix for a slow server has nothing in common with the fix for a late-discovered image.
What it is
Largest Contentful Paint (LCP) measures the render time of the largest image or text block visible in the viewport, relative to when the page started loading.
Good is ≤ 2.5 seconds at the 75th percentile. See Core Web Vitals.
The element is chosen by the browser, and it changes as the page loads — the LCP candidate updates until the user first interacts. Candidates: <img>, <image> inside SVG, <video> poster frames, an element with a CSS background-image, and block-level text nodes.
The four phases
The essential diagnostic. LCP decomposes into four intervals, and the fix depends entirely on which one dominates.
│──────────│─────────────│──────────────│──────────────│
TTFB resource resource element
load delay load time render delay
server time between downloading time between
+network TTFB and the the resource download and
fetch starting paint
Load delay is the most commonly dominant and the most commonly misdiagnosed. Teams compress an image that was never the problem, because the browser didn’t start fetching it until 1.8 seconds in.
The ordinary causes
In rough order of how often they’re the real answer:
The LCP image is a CSS background-image. Not discoverable by the preload scanner, so the fetch starts only after CSS parses — see Document Parsing
The LCP image is lazy-loaded.loading="lazy" on the hero is a self-inflicted delay of hundreds of milliseconds. Never lazy-load anything above the fold
The image is injected by JavaScript — a carousel or personalisation widget. Discovery waits for a script to download, parse and execute
Render-blocking CSS or JS delays paint even though the image arrived
A web font is the LCP element’s font, and text is invisible until it loads — Font Loading
Slow server — the floor everything else sits on
Fixing it in order
Measure the phases, don’t guess. Chrome DevTools’ Performance panel breaks LCP down; the web-vitals library exposes the same attribution in the field
Make it discoverable. LCP image in the markup as <img>, with fetchpriority="high":
The LCP element differs by viewport. Mobile’s largest element is often not desktop’s. Diagnose separately
It differs by template. Category pages, product pages and the homepage have different LCP elements and usually different causes. Site-wide averages hide this
Optimising the wrong element. Check what the browser actually selected before spending a sprint on it — DevTools names it
Lab and field disagree. Lab runs one connection on one device; the field is your real distribution. Trust the field for whether, the lab for why — Field vs Lab Data
A carousel whose first slide is the LCP element makes LCP dependent on carousel initialisation, which is usually JavaScript, which is usually late
The unglamorous fact
For most retail sites the single largest LCP win is serving a correctly-sized, modern-format hero image from markup, not lazy-loaded, over a CDN. That’s four changes, none of them interesting, and they typically outperform any amount of JavaScript optimisation.