LCP phase breakdown
The LCP attribution sub-parts: TTFB, resource load delay, resource load time, element render delay.
At a glance the headline numbers for LCP phase breakdown
The LCP attribution sub-parts: TTFB, resource load delay, resource load time, element render delay.
The median site spends 366ms of its 2.5s LCP budget: Render delay is the biggest piece at 224ms. computed
Where the milliseconds go the phases at the median, against the budget
At the median the whole stack uses 366ms, inside the 2.5s LCP budget. The biggest piece is Render delay at 224ms, 61% of the total. computed
Passing CLS per phase the share of sites passing, by how long each phase takes
Each line is one phase: left = sites where it is quick, right = sites where it drags (buckets, short to long). The steeper the fall, the more that phase decides CLS.
Render delay falls hardest: from its fastest to its slowest bucket the CLS pass rate drops 5 points, down to 81%. computed
The 2 distributions how each phase spreads across sites - color is the share passing in that range
Load time stays green across its whole range: it never decides CLS. Load time turns red in its tail - the failing sites live there. computed
Why this matters for the Core Web Vitals, and where to start fixing it
The LCP is a sum of four waits. TTFB is the server and the network producing the HTML. Load delay is the gap before the LCP resource even starts downloading, which is mostly a discovery problem. Load time is the download itself. Render delay is the gap between having the resource and painting it, usually render-blocking CSS or a font.
The split tells you which fix applies. A TTFB problem lives in hosting and caching. A load delay problem lives in markup and hints. A load time problem lives in image weight. A render delay problem lives in the head of the document. Optimizing the image while TTFB is the bottleneck does nothing.
Chrome field data from 189,915 sites, representing millions of real page loads. How we measured.
Live queries (6) — admin only
SELECT COUNT(*) AS count,
quantile_disc(m.lcp.timing.ttfb, 0.10) AS p10,
quantile_disc(m.lcp.timing.ttfb, 0.25) AS p25,
quantile_disc(m.lcp.timing.ttfb, 0.50) AS p50,
quantile_disc(m.lcp.timing.ttfb, 0.75) AS p75,
quantile_disc(m.lcp.timing.ttfb, 0.90) AS p90,
quantile_disc(m.lcp.timing.ttfb, 0.99) AS p99
FROM sites WHERE m.lcp.timing.ttfb IS NOT NULL;
SELECT COUNT(*) AS count,
quantile_disc(m.lcp.timing.load_delay, 0.10) AS p10,
quantile_disc(m.lcp.timing.load_delay, 0.25) AS p25,
quantile_disc(m.lcp.timing.load_delay, 0.50) AS p50,
quantile_disc(m.lcp.timing.load_delay, 0.75) AS p75,
quantile_disc(m.lcp.timing.load_delay, 0.90) AS p90,
quantile_disc(m.lcp.timing.load_delay, 0.99) AS p99
FROM sites WHERE m.lcp.timing.load_delay IS NOT NULL;
SELECT COUNT(*) AS count,
quantile_disc(m.lcp.timing.load_time, 0.10) AS p10,
quantile_disc(m.lcp.timing.load_time, 0.25) AS p25,
quantile_disc(m.lcp.timing.load_time, 0.50) AS p50,
quantile_disc(m.lcp.timing.load_time, 0.75) AS p75,
quantile_disc(m.lcp.timing.load_time, 0.90) AS p90,
quantile_disc(m.lcp.timing.load_time, 0.99) AS p99
FROM sites WHERE m.lcp.timing.load_time IS NOT NULL;
SELECT CASE WHEN m.lcp.timing.load_time >= 0 AND m.lcp.timing.load_time < 100 THEN 0 WHEN m.lcp.timing.load_time >= 100 AND m.lcp.timing.load_time < 200 THEN 1 WHEN m.lcp.timing.load_time >= 200 AND m.lcp.timing.load_time < 300 THEN 2 WHEN m.lcp.timing.load_time >= 300 AND m.lcp.timing.load_time < 400 THEN 3 WHEN m.lcp.timing.load_time >= 400 THEN 4 END AS bucket_idx, COUNT(*) AS n,
quantile_disc(crux."phone".cls, 0.5) AS median,
COUNT(*) FILTER (WHERE crux."phone".cls IS NOT NULL AND crux."phone".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE crux."phone".cls IS NOT NULL), 0) AS good_pct
FROM sites WHERE m.lcp.timing.load_time IS NOT NULL GROUP BY bucket_idx;
SELECT COUNT(*) AS count,
quantile_disc(m.lcp.timing.render_delay, 0.10) AS p10,
quantile_disc(m.lcp.timing.render_delay, 0.25) AS p25,
quantile_disc(m.lcp.timing.render_delay, 0.50) AS p50,
quantile_disc(m.lcp.timing.render_delay, 0.75) AS p75,
quantile_disc(m.lcp.timing.render_delay, 0.90) AS p90,
quantile_disc(m.lcp.timing.render_delay, 0.99) AS p99
FROM sites WHERE m.lcp.timing.render_delay IS NOT NULL;
SELECT CASE WHEN m.lcp.timing.render_delay >= 100 AND m.lcp.timing.render_delay < 200 THEN 0 WHEN m.lcp.timing.render_delay >= 200 AND m.lcp.timing.render_delay < 300 THEN 1 WHEN m.lcp.timing.render_delay >= 300 AND m.lcp.timing.render_delay < 400 THEN 2 WHEN m.lcp.timing.render_delay >= 400 AND m.lcp.timing.render_delay < 500 THEN 3 WHEN m.lcp.timing.render_delay >= 500 AND m.lcp.timing.render_delay < 600 THEN 4 WHEN m.lcp.timing.render_delay >= 600 THEN 5 END AS bucket_idx, COUNT(*) AS n,
quantile_disc(crux."phone".cls, 0.5) AS median,
COUNT(*) FILTER (WHERE crux."phone".cls IS NOT NULL AND crux."phone".cls <= 0.1) * 1.0 / NULLIF(COUNT(*) FILTER (WHERE crux."phone".cls IS NOT NULL), 0) AS good_pct
FROM sites WHERE m.lcp.timing.render_delay IS NOT NULL GROUP BY bucket_idx;