Why your LCP image loads late
By Malte Warncke, web performance engineer. Published .
Your LCP image usually loads late because the browser finds it late, not because the file is too big. On 146 slow sites I audited (phone visits, Chrome real-user data, September–October 2026), the median site’s main image waited 1.29 s before its download started, then downloaded in 0.17 s. The usual fix: let the browser find it sooner.
Largest Contentful Paint (LCP) is the time until the biggest element on screen, usually the main image or heading, has appeared. On 146 of the 164 slow sites I audited, that element was an image. Until it shows, the page looks unfinished to the visitor. Google counts an LCP of 2.5 seconds or less as good (opens in a new tab) (as of 2026), and its Search documentation says that Core Web Vitals, LCP among them, are used by its ranking systems (opens in a new tab).
So on those sites, a smaller file barely helps. Letting the browser find the image sooner does, and that change is usually small, though not always enough on its own.
What is LCP resource load delay?
Resource load delay is the time between the first byte of the page arriving and the browser starting to download the LCP image. It is the waiting part, and on the slow sites I audited it was usually longer than the download itself.
Google’s guide to optimising LCP (opens in a new tab) splits LCP into four sub-parts that follow each other without gaps:
| Sub-part | What happens during it |
|---|---|
| Time to First Byte (TTFB) | The server answers and the first byte of the HTML arrives |
| Resource load delay | The browser has the first byte of the HTML but hasn’t requested the image |
| Resource load duration | The image downloads |
| Element render delay | The image has arrived but isn’t on screen yet |
The two sub-parts with “delay” in their name should be close to zero. The same guide puts each delay at under 10% of the LCP time on a well-optimised page.
Here is what I measured on the homepages of the slow sites I audited whose LCP element is an image. The data comes from the Chrome UX Report (CrUX), Google’s dataset of real visits in Chrome, at the 75th percentile: the value that three in four phone visits are at or under.
| Main image, slow sites I audited | Value | Sites |
|---|---|---|
| Median wait before the download starts | 1.29 s (1,293 ms) | 146 |
| Median download time | 0.17 s (171 ms) | 146 |
| The image waited longer than it took to download | 93% (136) | 146 |
| The image waited more than 1 s | 60% (87) | 146 |
Each figure is the median across sites of each site’s 75th-percentile value for that sub-part. Those medians don’t add up to an LCP value, so don’t add the rows together.
Why does the LCP image wait?
Because the browser finds it late, or finds it and is told to hold back. Browsers use a preload scanner (opens in a new tab), a quick pass over the raw HTML that requests files before the main parser reaches them. Google’s guide says the LCP image has to be discoverable in that first HTML response to start early.
When I went back through my audits, I checked the stored Lighthouse runs of the slow sites I audited to see why the main image was found late. Lighthouse is Google’s lab test, the same one PageSpeed Insights runs. Each audit stored one mobile run (Lighthouse 13, 21 September to 7 October 2026), and 111 of the 146 sites had an image as the LCP element in that run too:
| What I found in the lab run | Share | Sites |
|---|---|---|
| Main image not in the initial HTML | 48% (53) | 111 |
| ↳ CSS background image | 45% (24) | 53 |
↳ <img> whose URL a JavaScript lazy-loader sets |
21% (11) | 53 |
↳ <img> inserted or set by JavaScript (inferred: raw HTML not stored) |
32% (17) | 53 |
| ↳ Video poster | 2% (1) | 53 |
| Main image lazy-loaded | 21% (23) | 111 |
| Main image inside a slider or carousel (keyword heuristic) | 28% (30) | 107 |
fetchpriority="high" on the main image |
23% (25) | 111 |
| Main image preloaded | 6% (7) | 111 |
| Found on time (eager, in the HTML, not a CSS background) | 42% (45) | 108 |
So on about half (53 of 111), the image wasn’t in the HTML at all. A fifth (23) lazy-loaded it, and more than a quarter (30 of 107) had it inside a slider. Only 7 preloaded it.
The image isn’t in the HTML
A CSS background image only becomes visible to the browser once the stylesheet has downloaded and been applied. An image that JavaScript adds, or whose address a script fills in from an attribute such as data-src, appears only after that script has downloaded and run. In both cases the browser has to wait for another file before it even knows the image exists.
The image is lazy-loaded
Lazy-loading means the browser holds an image back until it knows the image is on screen. With loading="lazy", that happens only after layout, so the request starts later than it could. Google’s guide is blunt about it: never lazy-load the LCP image. On the slow sites I audited, 11 of the 111 used the native loading="lazy" on the main image and 12 used a JavaScript lazy-loader.
The image sits in a slider
A slider makes the main image harder to predict. A lab run can pick a different LCP element from the one most visitors see, such as another slide, a personalised banner or a consent banner. The slider count above comes from a keyword heuristic, so treat it as approximate.
Isn’t the image just too big?
Usually not. When a team tells me “but the image is already compressed”, it usually is. On the slow sites I audited, the median main image was 89 KB and downloaded in 0.17 s, after waiting 1.29 s for the download to start. Compressing it further trims the short part; the wait is the long part.
| Main image size, slow sites I audited | Value | Sites |
|---|---|---|
| Median transfer size (lab) | 89 KB (middle half 30–173 KB) | 111 |
| Over 200 KB | 22% (24) | 111 |
Google’s guide adds two points. A smaller file shortens the download, but if nothing else changes, the saved time can move to the render delay and LCP stays where it was. And the download part tends not to be the bottleneck for most sites. If your image is one of the large ones, compress it as well, but look at the wait first.
How do I find out why my LCP image loads late?
Run Lighthouse and open two of its insights: “LCP breakdown” and “LCP request discovery”. I always run Lighthouse first to get a quick overview. PageSpeed Insights runs Lighthouse as well, so the same insights may already be in the report you were sent.
- LCP breakdown (opens in a new tab) shows the four sub-parts for that run, so you can see which one is the long one.
- LCP request discovery (opens in a new tab) checks whether the image is discoverable from the HTML, isn’t lazy-loaded and has
fetchpriority="high".
Check that the LCP element Lighthouse reports is the image your visitors see. As above, a lab run can measure a different element, and the real-user numbers in PageSpeed Insights come from many visits, not one run.
If you’d rather have someone trace this on your own site and tell you what to fix first, that is what my website performance audit is for.
How do I make the browser find the LCP image sooner?
Put the image in the HTML as a normal <img>, load it straight away and mark it as the important one. If it can’t be in the HTML, preload it.
- Put it in the HTML. An
<img>with itssrcorsrcsetin the HTML the server sends is visible to the preload scanner. If the image is a CSS background now, an<img>withobject-fit: covercrops the same way. If JavaScript inserts it, render at least the first image on the server. - Don’t lazy-load it. Remove
loading="lazy"from the main image, and give it a realsrcinstead of adata-srcthat a script swaps in. Google’s analysis of lazy-loading (opens in a new tab) found the best result came from loading images in the first viewport eagerly and lazy-loading the rest. - Mark it with
fetchpriority="high". Chrome starts most images at low priority and raises the ones in view only after layout (since Chrome 117, the first five large images start at medium). The Fetch Priority attribute (opens in a new tab) lets the main image start at high priority straight away. Google’s guide advises using it on one or two images at most; on more, it stops helping. - Preload it if it has to stay where it is. When the image must remain a CSS background or come from a script, a
<link rel="preload">(opens in a new tab) tells the browser about it early. Chrome’s insight asks forfetchpriority="high"on the preload as well. MDN advises against preloading several formats of the same image, because a browser that supports both downloads both. - In a slider, prioritise the first slide only. Make the first slide a normal
<img>in the HTML withfetchpriority="high", and give the later slidesfetchpriority="low", as Google’s guide suggests for slides that aren’t visible at first.
For most pages, steps 1 to 3 come down to one element:
<img
src="/images/hero-1200.jpg"
srcset="/images/hero-800.jpg 800w, /images/hero-1200.jpg 1200w"
sizes="100vw"
width="1200"
height="675"
alt="Describe what the image shows"
fetchpriority="high"
/>
There is no loading attribute, so the browser loads it straight away. When the image has to stay a background, Google’s guide puts the preload right after the stylesheet in the <head>, so both start loading together:
<link rel="stylesheet" href="/styles.css" />
<link rel="preload" as="image" href="/images/hero.jpg" fetchpriority="high" />
When is finding the image sooner not enough?
When the server answers slowly, or something else holds the image back after it has been found. Getting discovery right isn’t the whole story, and my data shows it.
| Median wait, slow sites I audited | Median wait | Sites |
|---|---|---|
| Main image not in the initial HTML | 1,603 ms | 53 |
| Main image in the initial HTML | 911 ms | 58 |
| Main image lazy-loaded | 1,546 ms | 23 |
| Main image not lazy-loaded | 1,099 ms | 88 |
Where the image was in the HTML, the wait was still a median of 0.91 s, against 1.60 s where it wasn’t. The groups overlap (an image can be both lazy-loaded and missing from the HTML), so these medians describe groups. They don’t measure what one cause costs, and my data can’t say what held back the images that were in the HTML.
Google’s guide names two reasons a discoverable image can still start late: the browser’s priority rules may rank other files higher, or the image sits on another domain and the browser has to open a connection to it first. Serve the main image from your own domain where you can. Beyond the wait, three other parts of LCP matter:
- The server. The image can’t be found before the HTML arrives. On the slow sites I audited, the median server response was 978 ms (TTFB at the 75th percentile on phones, 160 sites), while Google’s rough guide for a good TTFB (opens in a new tab) is 0.8 seconds or less.
- The render delay. The image has arrived but isn’t painted yet. Google’s guide lists the usual causes: a stylesheet or script in the
<head>that is still loading, an A/B testing script hiding the content, or long JavaScript tasks blocking the main thread. - The download itself, for the images that are large.
So start with discovery: on the slow sites I audited, the wait was usually longer than the download, and the fix is usually small. Then measure again, and look at the server and the render delay if LCP is still slow.
Where do these numbers come from?
From the full audits of 164 slow sites I audited between 16 September and 7 October 2026. I picked those sites because they looked slow, so the numbers describe slow sites, not websites in general. I don’t name or describe any of them.
- Wait, download and server times: CrUX phone data at the 75th percentile, over 28 days ending a few days before each run, for each homepage. 146 of the 164 have an image as the LCP element; 160 have server response data.
- Causes and image sizes: the Lighthouse mobile run each audit stored, one lab run per site, for the 111 of those 146 whose lab LCP element is also an image. Their wait times are still the real-user values.
The audit method and the other findings, from third-party scripts to page weight, are in my report Core Web Vitals in New Zealand, 2026.
Want to know what slows your own site down? Send me your URL and I'll check it for free.