Good LCP
The current good threshold is LCP at or below 2.5 seconds at the 75th percentile, evaluated separately for mobile and desktop.
Web Performance · LCP · Root-cause debugging
I do not start an LCP investigation by compressing every image or chasing a Lighthouse score. I start with the real LCP element and field signal, then ask where the time is actually spent: server response, discovery, transfer, or rendering. The fix should target that subpart—and production field data should decide whether it worked.

The short answer
Fix LCP by decomposing it before optimizing it. Confirm the real LCP candidate, then split its time into TTFB, resource load delay, resource load duration and element render delay. A large image can absolutely be the problem—I have encountered LCP images above 4–5 MB—but field data shows that slow origins often lose even more time before the image request starts. Make the meaningful hero content cheap to render, make critical resources discoverable and correctly prioritized, remove server/rendering bottlenecks, and validate the change in RUM/CrUX rather than declaring victory from one lab run.
The current good threshold is LCP at or below 2.5 seconds at the 75th percentile, evaluated separately for mobile and desktop.
LCP can be decomposed into TTFB, resource load delay, resource load duration and element render delay.
In Google's image-LCP field analysis, median p75 resource load delay for poor-LCP origins was about 1.29 s versus about 350 ms of image load duration.
web.dev recommends 0.8 seconds or less as a rough TTFB guide for most sites; TTFB itself is not a Core Web Vital.
Diagnostic model
The metric is one number, but the root cause is normally one or more distinct phases.
LCP is the render time of the largest eligible image, text block or video visible in the viewport. A good result is ≤2.5 seconds at p75. The useful debugging move is to stop treating that number as one opaque delay. I split it into TTFB → resource load delay → resource load duration → element render delay. That immediately changes the investigation from ‘make the page faster’ into a falsifiable question: which phase is consuming the time?
Custom diagram
A timeline from navigation to the final LCP paint: server response, waiting before the LCP request, resource transfer and waiting before the element can render.
Candidate first
You cannot configure an LCP selector, but your hero architecture strongly influences which eligible element becomes the largest visible candidate.
Saying that an LCP element can be ‘chosen’ sounds odd, but there is an important engineering truth behind it. The browser chooses from eligible visible content and can replace the candidate as larger content renders. Developers choose the hero composition, visual hierarchy, dimensions, rendering order and whether the dominant content is a text block, image, video poster or background image. In practice, I have redesigned hero sections so the meaningful primary content is a fast text block rather than forcing a huge decorative image to dominate the viewport. That is legitimate information architecture; shrinking or hiding useful content only to game LCP is not.
Use the device/viewport that approximates the affected field cohort. Candidate size depends on what is visible in the viewport.
Pass criterionThe test notes include route, viewport and test conditions.
Record the page in Chrome DevTools Performance and select the LCP marker or the LCP insight.
PathChrome DevTools → Performance → Record → Insights → LCP by phase / LCP badge
Pass criterionThe Details pane identifies the LCP candidate and timing breakdown.
Compare the LCP candidate with the actual primary hero content. Ask whether a decorative media asset is unnecessarily larger than the information the user came to see.
Pass criterionYou can explain why this element is the largest meaningful eligible content, or document a design reason to change the hero hierarchy.
LCP candidates can change as later content renders. After changing layout or media, record a new trace and verify the final candidate rather than assuming it stayed the same.
Pass criterionThe final LCP candidate is explicitly confirmed in the new trace.
Custom diagram
Two hero architectures: a heavy decorative media element dominates the viewport versus a meaningful fast text-led hero with supporting media that does not unnecessarily become the largest candidate.
Evidence before fixes
The same 4-second LCP can require completely different engineering work depending on which phase is dominant.
Google’s field analysis is a useful warning against single-cause thinking. For origins with poor image LCP, the median p75 breakdown was about 2,270 ms TTFB + 1,290 ms image load delay + 350 ms image load duration + 360 ms render delay. The majority of poor-LCP origins spent under 10% of p75 LCP time actually downloading the LCP image. That is population-level evidence, not a diagnosis of your page—but it shows why ‘compress the hero’ can be correct and still miss the biggest bottleneck.
| Dominant subpart | What it usually points toward | First evidence to inspect | Typical first move |
|---|---|---|---|
| TTFB | Redirects, connection latency, cache miss, slow origin/backend | Navigation timing, CDN/cache status, Server-Timing where available | Shorten redirects; improve caching/CDN/origin response |
| Resource load delay | LCP asset discovered late or has weak priority | Network initiator chain, request start relative to HTML, Priority column | Expose the asset in HTML/preload and prioritize the real LCP resource |
| Resource load duration | Too many bytes, poor dimensions/format, slow delivery | Transferred bytes, selected srcset candidate, cache/CDN | Serve the right dimensions/format and reduce transfer cost |
| Element render delay | Render-blocking CSS, fonts, main-thread work, client rendering/hydration | Performance trace after resource responseEnd | Remove the blocking work or render meaningful hero HTML earlier |
Resource load duration
Oversized images remain one of the simplest and most common regressions on sites without a disciplined media pipeline.
The most ordinary LCP failure I encounter is still an oversized image. On smaller ecommerce and content sites where uploads accumulate without a performance budget, I have seen LCP images exceed 4–5 MB. That number is my project observation, not a recommended threshold. The diagnosis is simpler: compare the rendered dimensions with the selected source candidate and transferred bytes. If a 700 px hero is downloading a multi-megabyte 3000 px original, you have an avoidable transfer problem.
In DevTools Network, inspect transferred size and the resource selected by srcset/sizes at the target viewport and DPR.
PathChrome DevTools → Network → Img → LCP request
Pass criterionThe evidence records rendered size, selected source and transferred bytes.
Provide responsive candidates rather than shipping one oversized master to every viewport. Use modern formats when they materially reduce bytes and preserve quality.
Pass criterionThe target viewport selects a candidate close to its display requirement instead of the full master asset.
Keep width and height (or an equivalent reserved aspect ratio) so image optimization does not create layout instability.
Pass criterionThe image has reserved dimensions and the change does not introduce CLS.
Verify that resource load duration actually fell and check whether the bottleneck moved to load delay or render delay.
Pass criterionThe before/after trace shows the intended subpart reduction without a new dominant delay.
<picture>
<source
type="image/avif"
srcset="/hero-640.avif 640w, /hero-1280.avif 1280w"
sizes="(max-width: 720px) 100vw, 1200px">
<img
src="/hero-1280.webp"
srcset="/hero-640.webp 640w, /hero-1280.webp 1280w"
sizes="(max-width: 720px) 100vw, 1200px"
width="1200"
height="675"
fetchpriority="high"
alt="Describe the meaningful hero content">
</picture>Markup syntax checked on 2026-09-08. Replace file paths, breakpoints, dimensions and alt text with values from the actual layout. Do not add high priority to multiple competing images.
Resource load delay
A 200 KB image requested 1.3 seconds late can lose more LCP time than a larger image discovered immediately.
Resource load delay is the time after TTFB but before the LCP resource request starts. Common causes are an image injected by JavaScript, a source hidden behind a lazy-loader data attribute, or a CSS background that cannot be discovered until CSS is fetched and parsed. Google’s field analysis also found a clear relationship between dependency-chain length and LCP: median LCP in the cited dataset rose from about 2.15 s with zero dependent requests to 2.54 s with one and 2.85 s with two. Make the browser discover the real LCP resource as close to the initial HTML as the architecture allows.
Custom diagram
A fast path where the parser sees the LCP image immediately versus a delayed path where HTML waits on CSS or JavaScript before the browser learns the image URL.
<!-- Best case: the LCP image is directly discoverable in HTML. -->
<img src="/hero.webp" width="1200" height="675" fetchpriority="high" alt="...">
<!-- If the LCP is a CSS background or otherwise not parser-discoverable: -->
<link
rel="preload"
as="image"
href="/hero-background.webp"
fetchpriority="high">Markup syntax checked on 2026-09-08. Do not blindly preload an image already discovered early, and make sure preload URL/type/candidate matches the resource the page actually uses to avoid duplicate or wasted fetches.
Network contention
Offscreen does not always mean ‘not requested yet’; browser heuristics can still let nearby images compete for bandwidth.
Another pattern I check on image-heavy pages is competition from media just below the first viewport. It can look harmless in the design, yet several nearby images may begin fetching early and consume bandwidth while the hero is still loading. Browser lazy-loading uses a calculated distance from the viewport rather than a hard ‘below the fold’ line. Chrome’s Fetch Priority guidance specifically calls out carousels where offscreen images may be considered close enough to load; lower priority can be appropriate for those noncritical neighbors.
| Image role | Typical loading choice | Priority hint | What to verify |
|---|---|---|---|
| Actual LCP hero | Eager / normal parser discovery | High when it is truly critical | Starts early; no competing high-priority images |
| Second/third carousel slide | Depends on UX; may still load early | Low can be appropriate | Does not delay first visible slide/LCP |
| Clearly below-fold editorial/product image | Lazy | Auto or Low | No initial network contention; appears before user reaches it |
| CSS background LCP | Preload because parser cannot see it in HTML | High on the preload when justified | One matching request starts early; no duplicate fetch |
Server + render path
If the HTML arrives late or the page cannot paint after the LCP resource is ready, image compression has diminishing returns.
TTFB sits in front of every later LCP phase. web.dev gives ≤0.8 s as a rough guide for most sites, while stressing that TTFB is not itself a Core Web Vital. In the field dataset above, poor-LCP origins had a median p75 TTFB of about 2.27 s—already close to the entire good-LCP budget. At the other end, element render delay appears when the LCP resource is ready but CSS, fonts, long main-thread work, client rendering or hydration still prevent the element from painting.
Record TTFB and check redirects, CDN/cache behavior and origin response. A slow HTML response shifts every later phase.
Pass criterionYou have a before value and can state whether TTFB is materially constraining the 2.5-second target.
In the trace, compare resource completion with the LCP paint. A large gap after the resource response indicates render delay, not download delay.
PathChrome DevTools → Performance → LCP by phase → Element render delay
Pass criterionThe trace shows whether the element waits after the resource is available.
Inspect render-blocking styles, web-font behavior, long tasks and JavaScript-driven rendering/hydration inside that interval.
Pass criterionAt least one observed blocker overlaps the render-delay window; do not infer causality from file size alone.
For client-rendered pages, prefer server-rendered/static meaningful hero HTML when it removes a real render dependency. Measure the trade-off rather than assuming SSR is automatically faster.
Pass criterionThe changed architecture paints the same meaningful content earlier in the trace and does not regress other CWV/UX signals.
Field attribution
CrUX confirms the field problem; first-party attribution can connect poor LCP to the target, resource and timing phase.
The `web-vitals` attribution build exposes the LCP target, image URL where applicable and the same four timing components used in this guide. That is far more actionable than collecting only `LCP=4.1s`. Keep the payload privacy-safe and normalized; the goal is to group templates, releases and candidate types, not to collect page text or user identifiers. For the broader field-vs-lab methodology, use the Core Web Vitals testing guide.
import {onLCP} from 'web-vitals/attribution';
onLCP(({value, rating, id, attribution}) => {
const payload = {
value,
rating,
id,
path: location.pathname,
target: attribution.target ?? null,
resource: attribution.url ?? null,
timeToFirstByte: attribution.timeToFirstByte,
resourceLoadDelay: attribution.resourceLoadDelay,
resourceLoadDuration: attribution.resourceLoadDuration,
elementRenderDelay: attribution.elementRenderDelay
};
navigator.sendBeacon(
'/rum/lcp',
new Blob([JSON.stringify(payload)], {type: 'application/json'})
);
});Syntax checked on 2026-09-08. Requires web-vitals v5 attribution build and a site-specific endpoint. Normalize routes and review consent, sampling, retention and sensitive-data policy before production collection.
| Field | Decision it supports | Privacy/quality note |
|---|---|---|
| Normalized route/template | Which page family is failing? | Strip IDs and sensitive query parameters |
| LCP target/candidate type | Is the regression text, image or another candidate pattern? | Prefer stable generated selectors; do not capture DOM text |
| Four LCP subparts | Which phase dominates for real users? | Aggregate by sufficient sample sizes |
| Release/build ID | Did a deployment introduce or remove the regression? | Use a technical build identifier, not a user identifier |
Metricum Lab method
The work is complete only when the causal chain is explicit and the production distribution moves in the intended direction.
My preferred order is deliberately conservative. I first confirm that LCP is a real user problem, then identify the candidate and dominant subpart, reproduce that mechanism, change one causal lever and compare the trace again. A faster lab run is the deployment gate—not the final proof. Production RUM can show the result quickly for your measured cohort; CrUX changes more slowly because it is an aggregated field dataset. I also check CLS after hero/image changes and use the JavaScript SEO debugging workflow when rendering architecture is involved.
Record p75 LCP for the affected mobile/desktop population and the measurement window. Add first-party RUM if available.
Pass criterionThe issue is tied to a real field cohort rather than a single lab score.
Record the final LCP element/resource at the target viewport and journey.
Pass criterionThe investigation names the actual candidate, not an assumed hero asset.
Use the LCP timing breakdown to classify the first root-cause hypothesis as TTFB, load delay, load duration or render delay.
Pass criterionThe hypothesis is tied to measured milliseconds in one phase.
Examples: right-size the selected image candidate, expose the resource in HTML, adjust priority, shorten server response or remove observed render blocking.
Pass criterionThe patch can be connected to the dominant subpart without unrelated performance changes.
Re-run equivalent traces to distinguish a real subpart reduction from normal run-to-run variation.
Pass criterionThe intended subpart improves across repeated comparable runs and no major regression appears elsewhere.
Watch first-party RUM by route/release; then use CrUX/Search Console as slower external field confirmation. Do not treat the rolling field dataset as an instant deployment test.
Pass criterionThe production cohort moves in the expected direction with adequate sample and no hidden device/template regression.
Primary sources and documentation
Every changing search, browser, interface or technical-behavior claim in this guide is tied to a current primary source.
Need help diagnosing and implementing the fix?
Metricum Lab can connect field LCP regressions to server timing, resource discovery, image delivery, render-path traces and post-release validation—then prioritize the fixes that actually reduce user-visible delay.
Explore Core Web Vitals Optimization