Lazy-loading the hero image delays LCP instead of speeding it up

Chart from Calibre: Native lazy loading: With the default, the LCP image request started…
Chart: Calibre, "Native lazy loading"

Summary

Below-the-fold images are safe to lazy-load in every major browser, because each one starts the download before the image reaches the screen. The hero image is the exception.

A lazy hero waits for layout before the browser requests it, and in a test by Calibre, a performance monitoring vendor, the request started more than twice as late. Check the rendered HTML of each template for loading="lazy" on the first large image and remove it.

Lazy-loading is meant to make pages faster, but on the hero image it does the opposite. The browser cannot request a lazy image until it has laid out the page, so the image that sets Largest Contentful Paint (LCP, the time until the biggest element on the first screen appears) arrives later, not sooner. Calibre’s guide to native lazy loading measured the delay and reports that 17% of pages lazy-load that image anyway. Calibre, which sells web performance monitoring, also lists how far each browser fetches ahead for images further down the page.

In Calibre’s test, the hero image request started at 177ms with the default setting, while the stylesheet was still downloading. With loading="lazy" the request did not start until 439ms, just after the stylesheet was applied and layout could run. The cause is how browsers find images. The preload scanner, the part of the browser that reads raw HTML early and starts downloads, fetches an eager image straight away. A lazy image has to wait until the browser has worked out where it sits on the page. On a mobile connection the stylesheet takes longer to arrive, so the hero waits longer too.

Further down the page, the cost is small

Below the fold, lazy loading is cheap in every browser because none of them waits for the image to appear. Each starts the download at some distance before the image reaches the screen, and Calibre’s post lists the distances:

BrowserImagesIframesVideo and audio
Chrome, 4G1,250px2,500px1,250px
Chrome, 3G2,500px3,500px2,500px
Chrome, 2G6,000px6,000px6,000px
Firefox600px600pxNot supported
SafariOne viewport0pxNot supported

Chrome fetches further ahead on slower connections. Firefox holds one fixed margin of 600px on every side, whatever the connection. Safari gives images a full viewport in each direction but does not load a lazy iframe until it is on screen.

Safari’s iframe rule is the one most likely to show. Calibre does not measure it, but the likely result is that a slow embed just below the fold appears blank for a moment in Safari. Firefox and Safari also ignore loading="lazy" on video and audio, so those elements load straight away there.

Calibre’s warning is aimed at templates. Its post compares a blanket loading="lazy" in a shared template to preloading everything: both hurt performance, and the attribute lands on images nobody tagged by hand, the hero included.

What to do

  1. Open the LCP element on each main template in the DevTools Elements panel and check for loading="lazy". Read the rendered HTML, not the template source, since a component or framework may add the attribute.
  2. Leave the hero eager, which is the default, and add fetchpriority="high" to it, as Calibre recommends. Keep lazy loading for media below the first screen.
  3. Give every lazy image, video and iframe a width and height, or a CSS aspect-ratio. Lazy media arrives after layout, and without dimensions it pushes content around and adds Cumulative Layout Shift.
  4. With <picture>, put loading and fetchpriority on the <img>. The browser ignores them on <source>.
<!-- Hero: no loading attribute, so it stays eager -->
<img src="hero.avif" width="1200" height="675" fetchpriority="high" alt="...">

<!-- Further down the page -->
<img src="chart.avif" width="800" height="450" loading="lazy" alt="...">

To confirm that below-the-fold media is deferred, open the Network panel and filter to Img and Media. Iframes show up under Doc. Tick Disable cache and reload without scrolling. Everything in that list loaded with the page, and whatever arrives once you scroll was deferred. This check will not catch a lazy hero. A lazy image already on the first screen still loads without scrolling, just later, so the Elements panel check in step 1 is the one to rely on.