Web development

Lazy loading images in HTML: loading="lazy", fetchpriority and decoding

How to enable deferred image loading with loading="lazy", why you must not apply it to the main image, and how to combine it with fetchpriority and decoding.

Lazy loading images means not downloading an image until it is about to enter the viewport. In HTML you enable it by adding loading="lazy" to the <img> tag, with no JavaScript or libraries, and every current browser supports it. The most important rule is not to apply it to the page’s main image (the one that counts for LCP), which must load as early as possible.

This article explains how the attribute works, how to combine it with fetchpriority and decoding, which mistakes turn it into a penalty rather than an improvement, and how to check that it is working.

What loading=“lazy” actually does

When the browser finds an image with loading="lazy", instead of downloading it immediately it waits until the user scrolls close to it. The distance at which the download starts is up to the browser (in Chrome it depends on the connection type and is usually several hundred pixels), so the image is normally ready by the time it appears.

<img src="/img/chart.webp" alt="Monthly visits chart" width="800" height="500" loading="lazy" decoding="async" />

The attribute takes two values:

  • lazy: defers loading until the image is near the viewport.
  • eager: loads immediately (the default if you specify nothing).

According to Can I use, support is universal in modern browsers: Chrome since version 77, Firefox since 75, Safari since 15.4 and Edge since 79. An older browser that does not understand it simply ignores the attribute and loads the image as always; there is no risk of the image not showing.

Why it matters: fewer bytes and a better LCP

A page with twenty images that only shows three on screen downloads all twenty without lazy loading. With loading="lazy" on the off-screen ones, it downloads three and the rest as needed. The result is less transfer, less competition for bandwidth and, most relevant for the Core Web Vitals, critical resources (the main image, fonts, CSS) arrive sooner.

The golden rule: never defer the LCP image

LCP (Largest Contentful Paint) measures when the largest visible element is painted. In most articles and product pages that element is the featured image. If you give it loading="lazy", the browser treats it as low priority and loads it later than it could: LCP gets worse. The web.dev guide to optimising LCP is explicit about this.

The right thing for the main image is the opposite:

<img src="/img/cover.webp" alt="Article cover" width="1200" height="630" loading="eager" fetchpriority="high" decoding="async" />
  • loading="eager" (or simply omitting the attribute) so it downloads immediately.
  • fetchpriority="high" to tell the browser this image matters more than other resources of the same kind.

Rule of thumb: the image that is already visible when the page loads, without scrolling, is not deferred. The rest are.

fetchpriority and decoding: the other two attributes

fetchpriority

fetchpriority adjusts the relative priority of a request. It accepts high, low and auto. Use high on the LCP image and, optionally, low on decorative carousel images not visible at the start. Putting it on every image is pointless: if everything is a priority, nothing is.

decoding

decoding="async" lets the browser decode the image off the main thread, so the rest of the page does not wait. It is safe to use on practically every content image. The alternative sync only makes sense in very specific cases where the image must appear in exactly the same frame as the surrounding text.

Always with width and height

Lazy loading and dimensions go together. If the browser does not know how much space an image it has not downloaded yet will take, it reserves zero pixels and, when the image arrives, pushes the content down. That is a layout shift (CLS), another of the Core Web Vitals. Always declare width and height with the image’s real dimensions; CSS can scale it (max-width: 100%; height: auto;) without losing the proportions.

CSS background images and iframes

The loading attribute only exists on <img> and <iframe>. Background images defined in CSS (background-image) cannot be deferred with it; if you need to, you will have to use JavaScript with IntersectionObserver, or reconsider whether that image should be an <img> in the HTML (which is also better for accessibility and SEO).

For iframes (embedded videos, maps) it works the same way:

<iframe src="https://www.youtube-nocookie.com/embed/ID" title="Video title" width="560" height="315" loading="lazy"></iframe>

Common mistakes

  1. Adding loading="lazy" to every image automatically. Many WordPress plugins do this. Check that the featured image is excluded.
  2. No width or height. Produces CLS and, in some browsers, makes lazy loading behave worse because they cannot calculate the distance to the viewport.
  3. Images hidden with display: none. They still download as soon as the browser decides they are “near”. If they are not going to be shown, leave them out of the HTML.
  4. Expecting lazy loading to fix huge images. Deferring the load does not reduce the weight. Resize and convert to WebP or AVIF first; then defer.

How to check it works

Open the browser developer tools, go to the Network tab, filter by images and reload the page without scrolling. Only the visible images should appear. As you scroll down you will see the others being requested. In Lighthouse, the “Defer offscreen images” audit disappears when everything is correct, and “Largest Contentful Paint image was lazily loaded” warns you if you deferred the main image by mistake.

Conclusion

loading="lazy" is the simplest and safest way to reduce the initial weight of a page with many images: one attribute, no JavaScript and universal support. For it to be a real improvement, pair it with width and height, use decoding="async", and leave the main image out with loading="eager" and fetchpriority="high". Deferring what is not visible and prioritising what is, is essentially the whole strategy.

Sources and references

  1. MDN: Lazy loading developer.mozilla.org
  2. MDN: <img> — loading attribute developer.mozilla.org
  3. web.dev: Browser-level image lazy loading for the web web.dev
  4. web.dev: Optimize Largest Contentful Paint web.dev
  5. MDN: fetchpriority developer.mozilla.org
  6. Can I use: Lazy loading via attribute for images & iframes caniuse.com

Related tools

Herramientas gratuitas de AIMRAN Tools que funcionan en tu navegador, sin registro.

Ver todas las herramientas

Web development

How to convert images to WebP (and when not to)

What WebP is, how much it saves over JPEG and PNG, how to convert images in the browser, the terminal or Node.js, and how to serve them with picture.

4 min read