A slow Largest Contentful Paint does not automatically mean the LCP image is too large. LCP can be delayed before the browser discovers the resource, while that resource is loading, after it has finished loading, or even before the browser receives the first byte of HTML.
The first task is therefore not optimization. It is determining which part of the LCP timeline is responsible.
A useful LCP investigation decomposes the metric into server response, resource load delay, resource load duration and element render delay. Once the dominant stage is known, the investigation can move to the correct layer instead of applying generic fixes.
Why is LCP slow?
LCP can be slow because the initial HTML response arrives late, the LCP resource is discovered too late, the resource downloads slowly, or the browser cannot render it promptly because of CSS, JavaScript or main-thread work. The correct fix depends on which part of the LCP timeline is actually responsible.
Poor LCP Is a Symptom, Not a Root Cause
LCP is the final timing outcome of several stages.
A poor value tells you that the largest qualifying visible content appeared later than desired. It does not identify why.
Server response is late
The browser cannot begin processing the document until the HTML response starts arriving.
Resource discovery is late
The browser receives HTML but cannot request the LCP resource until a later dependency reveals it.
Resource loading is slow
The request starts early, but the bytes take too long to arrive.
Rendering is late
The resource is already available, but CSS, JavaScript, main-thread work or application state delays paint.
“Optimize the image” is not a diagnosis. It is only relevant when evidence shows image delivery is a meaningful part of the LCP bottleneck.
For the broader metric context, see Core Web Vitals.
First Identify the Actual LCP Element
Before diagnosing LCP, confirm which element the browser is actually reporting as the LCP candidate.
Depending on the page and browser behavior, the LCP element may be:
- an image;
- a text block;
- a CSS background image that qualifies for LCP;
- a video poster;
- another eligible content element.
Do not assume the visually largest hero is automatically the LCP element.
The candidate may also change during page load as larger eligible content is rendered.
Diagnosis begins with the measured LCP candidate, not the element that merely looks largest in the design.
See identify the LCP element for the dedicated guide.
Break LCP Into Its Delay Components
The current web.dev diagnostic model breaks LCP into four timing subparts:
TTFB → Resource Load Delay → Resource Load Duration → Element Render Delay → LCP
1. Time to First Byte
Time from navigation start until the browser receives the first byte of the HTML document response.
2. Resource Load Delay
Time between TTFB and the moment the browser starts loading the LCP resource.
3. Resource Load Duration
Time required to transfer the LCP resource itself.
4. Element Render Delay
Time between the LCP resource finishing and the LCP element actually rendering.
If the LCP element does not require a separate network resource—for example, some text-based LCP cases—the resource load delay and resource load duration can be zero.
This decomposition matters because shortening one stage does not always improve total LCP if the saved time simply shifts into another stage.
Cause 1: The Server Response Arrives Too Late
If the initial HTML arrives late, every downstream browser action begins later.
Possible contributors include:
- origin processing;
- cache MISS behavior;
- application execution;
- database work;
- upstream services;
- CDN-to-origin latency;
- network conditions.
A high document response time should not immediately become:
The correct next step is to determine where the response delay actually occurs.
If HTML delivery is already the dominant bottleneck, frontend-only LCP changes may have limited impact until the response path is addressed.
For the broader diagnosis method, see the root-cause performance investigation. For WordPress-specific cases, see diagnose high WordPress TTFB.
Cause 2: The LCP Resource Is Discovered Too Late
The browser cannot request a resource before it knows that resource exists.
An LCP resource may be discovered late when it is:
- inserted by JavaScript;
- referenced through a stylesheet;
- hidden behind another dependency;
- lazy-loaded unnecessarily;
- introduced after client-side rendering;
- created only after application logic runs.
HTML → CSS / JavaScript / Framework Work → LCP Resource
The longer the dependency chain before discovery, the later the request can begin.
This is why an image can be well compressed and still produce poor LCP: the browser may simply learn about it too late.
See late LCP discovery for the dedicated investigation.
Cause 3: The LCP Resource Takes Too Long to Load
If the browser discovers the LCP resource early but the request itself takes too long, the relevant stage is resource load duration.
Evidence may point toward:
- resource size;
- image dimensions or responsive candidate selection;
- network throughput;
- resource origin;
- CDN behavior;
- caching;
- connection cost;
- network contention;
- request priority.
This is where image optimization may become relevant—but only if the actual LCP resource is an image and transfer duration is meaningfully contributing to total LCP.
Do not automatically convert every LCP image to another format and call the problem solved. First prove that resource load duration is the stage that needs improvement.
Responsive images matter
The resource actually downloaded by the browser may be selected from srcset and sizes.
Diagnosis should therefore inspect the network request that was actually made, rather than assuming the CMS-visible image URL is the resource users received.
Cause 4: The Resource Loads but Rendering Happens Late
One of the most important LCP patterns is:
That gap is element render delay.
Possible causes include:
- render-blocking stylesheets;
- synchronous scripts;
- JavaScript execution;
- main-thread contention;
- the element not yet existing in the DOM;
- client-side rendering or hydration;
- the element being hidden until application state changes;
- style calculation and layout;
- font-related rendering behavior for text LCP;
- animation or delayed visibility.
Resource Request → Resource Complete ─────────────→ LCP Paint
↑ Element Render Delay ↑
When this pattern exists, compressing the resource further may reduce download time without materially improving total LCP.
For the dedicated path, see LCP image loads fast but LCP is still slow.
How the Network Waterfall Helps Diagnose LCP
A network waterfall helps establish the order of events.
For LCP diagnosis, it can answer questions such as:
- When did the HTML response begin?
- When did the LCP resource request start?
- Was the request discovered early or late?
- How long did the transfer take?
- Did another network dependency need to complete first?
The waterfall should not be treated as the entire diagnosis. It explains network timing, but it may not explain why paint occurred late after the network became quiet.
See read the network waterfall for the deeper workflow.
Use the Request Initiator to Find Late Discovery
If the LCP request begins late, the next question is:
The request initiator can help identify whether the request came from:
- the HTML parser;
- CSS;
- JavaScript;
- a framework;
- a tag manager;
- another dependency.
This can turn a vague finding such as “the LCP image starts late” into a stronger explanation:
The browser cannot discover the LCP resource until a stylesheet, script or application dependency exposes it.
When You Need a Performance Trace
A waterfall is excellent for request timing.
It becomes less sufficient when the network looks healthy but the LCP element still renders late.
A Performance Trace becomes useful when:
- the main thread is busy;
- JavaScript execution occurs before paint;
- style or layout work is significant;
- the resource is complete but rendering is delayed;
- client-side rendering controls when the element appears.
The trace provides evidence about browser work that a request waterfall alone cannot show.
Why Main-Thread Work Can Delay LCP
LCP is not purely a network metric.
The browser still has to perform work before pixels can appear.
If the main thread is occupied by:
- JavaScript execution;
- long tasks;
- style calculation;
- layout;
- other rendering work;
the LCP element may wait even though its resource has already arrived.
Network complete does not mean rendering complete.
Does Preloading the LCP Resource Help?
Preload is not a universal LCP fix.
It can be useful when:
- the resource is genuinely critical;
- the browser needs it early;
- normal discovery happens too late.
But preload may provide little benefit—or introduce new problems—when:
- the resource is already discovered early;
- the wrong responsive image candidate is preloaded;
- the browser performs duplicate or unnecessary work;
- too many resources are preloaded;
- the real bottleneck is render delay rather than discovery.
Before adding preload, prove that resource load delay is a meaningful part of the LCP timeline.
See preload the LCP image for the focused guide.
Lazy Loading and LCP
An above-the-fold LCP resource should not be unnecessarily delayed by lazy-loading behavior.
But that does not mean lazy loading should be disabled site-wide.
The correct scope is the resource that is critical to the initial viewport.
If an LCP resource is hidden behind JavaScript-based lazy loading or receives intentionally delayed loading behavior, investigate whether that delay is responsible for late request start.
CSS Background Images and LCP Discovery
A critical image referenced through CSS may be discovered differently from an image URL available directly in the initial HTML.
The browser may need to download and process the relevant stylesheet before it can discover the image.
This can increase resource load delay depending on the implementation.
That pattern belongs in the late LCP discovery investigation rather than a generic image-compression workflow.
Client-Side Rendering Can Delay LCP
If the LCP element only appears after:
- framework execution;
- hydration;
- an API response;
- component mount;
- application state initialization;
the asset itself may be relatively small and still appear late.
The bottleneck is then not simply transfer size. The browser may be waiting for application work before the element can exist or become visible.
LCP Candidate Changes During Page Load
The browser can update the LCP candidate as larger eligible content is rendered.
This matters because an optimization can change which element becomes the final candidate.
After making a change, confirm that:
- the expected element is still the actual LCP candidate;
- the timing being compared belongs to the same user-visible content;
- the improvement did not merely move LCP attribution to a different element.
Field LCP vs Lab LCP
Lab LCP and field LCP can differ because the environments are different.
Real users vary by:
Lab testing is useful for controlled reproduction and diagnosis. Field data shows what actual users experience across production conditions.
For the broader distinction, see Lighthouse vs Core Web Vitals.
A Practical LCP Diagnostic Framework
Identify the actual LCP element
Confirm the browser-reported candidate before optimizing.
Measure document response
Determine whether TTFB is already consuming a significant part of the LCP timeline.
Find when the LCP resource is discovered
Compare TTFB with the request start and inspect the initiator.
Measure resource load duration
Determine whether transfer time is materially contributing to LCP.
Compare resource completion with LCP paint
A large gap indicates render delay rather than transfer delay.
Inspect main-thread and rendering activity
Use a trace when network timing does not explain the delay.
Form a root-cause hypothesis
State which stage appears responsible and what evidence supports it.
Confirm with evidence
Look for supporting and contradicting evidence before treating the hypothesis as fact.
Fix the responsible layer
Apply the smallest targeted change that addresses the identified stage.
Repeat and verify
Re-test under comparable conditions and check for regressions.
LCP Symptom → Evidence → Likely Category
| LCP symptom | Evidence | Likely category | Next investigation |
|---|---|---|---|
| HTML arrives late | Document timing, TTFB, cache status | Server response / TTFB | Server, application, cache or origin path |
| LCP request starts late | Waterfall, initiator | Resource load delay / discovery | Late discovery and dependency chain |
| Request starts early but finishes late | Network timing, transfer size, resource selection | Resource load duration | Resource delivery, network, CDN, candidate selection |
| Resource finishes early but LCP is late | Performance trace, DOM/render timing | Element render delay | Main thread, CSS, JavaScript, rendering |
A Practical LCP Diagnostic Decision Tree
TTFB → Discovery → Load → Render
IS THE DOCUMENT RESPONSE LATE? │ ├─ YES │ └─ Investigate TTFB → server → application → cache → origin │ └─ NO ↓ DOES THE LCP RESOURCE START LATE? │ ├─ YES │ └─ Investigate discovery → initiator → dependency chain │ └─ NO ↓ DOES THE RESOURCE DOWNLOAD TAKE TOO LONG? │ ├─ YES │ └─ Investigate size → network → CDN → resource selection │ └─ NO ↓ IS THE RESOURCE COMPLETE WELL BEFORE LCP? │ ├─ YES │ └─ Investigate render delay → main thread → CSS → JS → application state │ └─ NO └─ Continue resource / network investigation
Common LCP Diagnosis Mistakes
Assuming LCP is always the hero image
Confirm the actual candidate first.
Only compressing the image
Compression helps only when transfer duration is a meaningful bottleneck.
Preloading every hero asset
Preload should follow evidence of late discovery or insufficient priority.
Ignoring TTFB
A late document can delay the entire loading path.
Ignoring request start time
A fast download can still start far too late.
Looking only at file size
File size does not explain discovery or render delay.
Ignoring the initiator
The initiator often reveals why the request starts late.
Ignoring the main thread
A completed resource can still wait for browser work before paint.
Running Lighthouse once
One synthetic run is a diagnostic sample, not definitive evidence.
Mixing field and lab conclusions
Use each dataset for the question it can answer.
Delaying all JavaScript
Blanket changes can break dependencies or move the bottleneck.
Removing CSS without evidence
Only target CSS that contributes to the measured rendering path.
Testing only the homepage
LCP candidates and dependencies can differ by template.
Not re-checking the LCP candidate
An optimization may change which element becomes LCP.
What Not to Fix Yet
Before changing production, do not assume:
Diagnosis first. Optimization second.
Fix the Responsible Layer, Not the Metric
| Root-cause category | Fix direction |
|---|---|
| High TTFB | Investigate server, cache, application, database or upstream response path |
| Late resource discovery | Expose the critical resource earlier in the loading path |
| Slow resource transfer | Improve resource delivery, selection, transfer size or network path |
| Element render delay | Investigate main-thread work, CSS, JavaScript, DOM visibility and rendering dependencies |
This matrix defines investigation direction. It is not an implementation checklist.
How to Verify an LCP Fix
A change is not complete until the intended bottleneck has been verified.
After remediation:
- repeat the test under comparable conditions;
- compare document timing;
- compare LCP request start and finish times;
- compare LCP paint timing;
- confirm the LCP candidate;
- inspect field trends when available;
- check for layout, functionality, tracking or rendering regressions.
If a change improves image transfer but total LCP stays the same, investigate where the saved time moved.
See verify the optimization for the dedicated workflow.
When a Deeper LCP Investigation Is Needed
A deeper investigation is useful when:
A poor LCP score tells you that important content appeared too late, but it does not tell you which layer caused the delay. When the timing spans server response, resource discovery, network delivery and browser rendering, a root-cause investigation can isolate the responsible stage before production changes are made.
Know LCP is slow, but not which stage is responsible?
A structured Core Web Vitals investigation can separate server response, resource discovery, transfer and render delay so the responsible layer is identified before production changes are made.
WebPerfLab — Measure → Trace → Diagnose → Fix → Verify
Final Takeaway
LCP optimization starts with timing decomposition.
Ask:
- Did the document arrive late?
- Was the LCP resource discovered late?
- Did the resource load slowly?
- Did rendering happen late?
Then fix the responsible layer.
Decompose→
Trace→
Confirm→
Fix→
Verify
Poor LCP is a symptom. Root-cause diagnosis determines the fix.