Browser-level image lazy loading for the web

Addy Osmani
Addy Osmani
Houssein Djirdeh
Houssein Djirdeh
Mathias Bynens
Mathias Bynens
Barry Pollard
Barry Pollard

Browser Support

  • Chrome: 77.
  • Edge: 79.
  • Firefox: 75.
  • Safari: 15.4.

You can use the loading attribute to lazy-load images without the need to write custom lazy-loading code or use a separate JavaScript library. Here's a demo of the feature:

Lazy-loaded images load as the user scrolls through the page.

This page walks through the details of implementing lazy-loading in the browser.

Why browser-level lazy loading?

According to the HTTP Archive, images are the most-requested asset type for most websites, and they usually take up more bandwidth than any other resource. At the 90th percentile, sites send over 5 MB of images on desktop and mobile.

Previously, there were two ways to defer the loading of off-screen images:

Either option can let developers include lazy loading behavior, and many developers have built third-party libraries to provide abstractions that are even easier to use.

With lazy loading supported directly by the browser, however, there's no need for an external library. Browser-level lazy loading also ensures that loading of images still works even if the client disables JavaScript. Note however that loading is only deferred when JavaScript is enabled.

The loading attribute

Chrome loads images at different priorities depending on where they're located relative to the device viewport. Images below the viewport are loaded with a lower priority, but they're still fetched as the page loads.

You can use the loading attribute to completely defer the loading of offscreen images:

<img src="image.png" loading="lazy" alt="…" width="200" height="200">

Here are the supported values for the loading attribute:

  • lazy: Defer loading of the resource until it reaches a calculated distance from the viewport.
  • eager: Default loading behavior of the browser, which is the same as not including the attribute and means the image is loaded regardless of where it's located on the page. This is the default, but it can be useful to set explicitly if your tooling automatically adds loading="lazy" when there's no explicit value, or if your linter complains if it isn't explicitly set.

Relationship between the loading attribute and fetch priority

The eager value is an instruction to load the image as usual, without delaying the load further if the image is off-screen. It doesn't load the image faster than another image that doesn't have a loading attribute.

If you want to increase the fetch priority of an important image (for example, the LCP image), use Fetch Priority with fetchpriority="high".

An image with loading="lazy" and fetchpriority="high" is still delayed while it's off-screen, and then fetched with a high priority when it's almost within the viewport. This combination isn't really necessary because the browser would likely load that image with high priority anyway.

Distance-from-viewport thresholds

All images that are immediately viewable without scrolling load normally. Images far below the device viewport are only fetched when the user scrolls near them.

Chromium's implementation of lazy loading tries to ensure that offscreen images are loaded early enough that they finish loading by the time the user scrolls to them by fetching them well before they become visible in the viewport.

The distance threshold varies depending on the following factors:

You can find the default values for the different effective connection types in the Chromium source. You can experiment with these different thresholds by throttling the network in DevTools.

Improved data-savings and distance-from-viewport thresholds

In July 2020, Chrome made significant improvements to align the image lazy loading distance-from-viewport thresholds to better meet developer expectations.

On fast connections (4G), we reduced Chrome's distance-from-viewport thresholds from 3000px to 1250px and on slower connections (3G or lower), changed the threshold from 4000px to 2500px. This change achieves two things:

  • <img loading=lazy> behaves closer to the experience offered by JavaScript lazy loading libraries.
  • The new distance-from-viewport thresholds still means images will probably have loaded by the time a user has scrolled to them.

You can find a comparison between the old versus new distance-from-viewport thresholds for one of our demos on a fast connection (4G) next:

The new and improved thresholds for image lazy loading, reducing the distance-from-viewport thresholds for fast connections from 3000px down to 1250px.
Comparison of the older versus newer thresholds used for browser-level lazy-loading.

and the new thresholds versus LazySizes (a popular JavaScript lazy loading library):

The new  distance-from-viewport thresholds in Chrome loading 90KB of images compared to LazySizes loading in 70KB under the same network conditions.
Comparison of the thresholds used for lazy-loading in Chrome and LazySizes.

Give your images dimension attributes

While the browser loads an image, it doesn't immediately know the image's dimensions, unless they're explicitly specified. To let the browser reserve enough space on a page for images, and avoid disruptive layout shifts, we recommend adding width and height attributes to all <img> tags.

<img src="image.png" loading="lazy" alt="…" width="200" height="200">

Alternatively, specify their values directly in an inline style:

<img src="image.png" loading="lazy" alt="…" style="height:200px; width:200px;">

The best practice of setting dimensions applies to <img> tags regardless of whether you're lazy loading them, but lazy loading can make it more important.

Lazy loading in Chromium is implemented in a way that makes images more likely to be loaded as soon as they're visible, but there's still a chance that they won't load at the right time. If that happens, not specifying width and height on your images increases their impact on Cumulative Layout Shift. If you can't specify your images' dimensions, lazy loading them can save network resources at the risk of these increased layout shifts.

In most scenarios, images still lazy load if you don't specify dimensions, but there are a few edge cases you should be aware of. Without width and height specified, image dimensions default to 0×0 pixels. If you have a gallery of images, the browser might decide that all of them fit inside the viewport at the start, because each image takes up no space and no image is pushed offscreen. In this case, the browser decides to load everything, making the page load more slowly.

For an example of how loading works with large numbers of images, refer to this demo.

You can also lazy-load images you've defined using the <picture> element:

<picture>
  <source media="(min-width: 800px)" srcset="large.jpg 1x, larger.jpg 2x">
  <img src="photo.jpg" loading="lazy">
</picture>

Although the browser decides which image to load from any of the <source> elements, you only need to add loading to the fallback <img> element.

Always eager-load images visible in the first viewport

For images that are visible when the user first loads the page, and especially for LCP images, use the browser's default eager loading so they can be available right away. For more information, see The performance effects of too much lazy-loading.

Use loading=lazy only for images outside the initial viewport. The browser can't lazy-load an image until it knows where the image should be on the page, which causes them to load more slowly.

<!-- visible in the viewport -->
<img src="product-1.jpg" alt="..." width="200" height="200">
<img src="product-2.jpg" alt="..." width="200" height="200">
<img src="product-3.jpg" alt="..." width="200" height="200">

<!-- offscreen images -->
<img src="product-4.jpg" loading="lazy" alt="..." width="200" height="200">
<img src="product-5.jpg" loading="lazy" alt="..." width="200" height="200">
<img src="product-6.jpg" loading="lazy" alt="..." width="200" height="200">

Graceful degradation

Browsers that don't support the loading attribute ignore it. They don't get the benefits of lazy loading, but there's no negative impact from including it.

FAQ

Some frequently asked questions about browser-level lazy loading.

Can I automatically lazy-load images in Chrome?

Previously, Chromium automatically lazy-loaded any images that were well suited to being deferred if Lite mode was enabled on Chrome for Android and the loading attribute was either not provided or set to loading="auto". However, Lite mode and loading="auto" have been deprecated and there are no plans to provide automatically lazy-load of images in Chrome.

Can I change how close an image needs to be to the viewport before it loads?

These values are hardcoded and can't be changed through the API. However, they might change in the future as browsers experiment with different threshold distances and variables.

Can CSS background images use the loading attribute?

No, you can only use it with <img> tags.

Using loading="lazy" can prevent images being loaded when they aren't visible but are within the calculated distance. These images might be behind a carousel or hidden by CSS for certain screen sizes. For example, Chrome, Safari, and Firefox don't load images using display: none; styling, either on the image element or on a parent element. However, other image hiding techniques, such as using opacity:0 styling, still cause the browser to load the image. Always test your implementation thoroughly to make sure it's acting as intended.

Chrome 121 changed the behavior for horizontal-scrolling images like carousels. These now use the same thresholds as vertical scrolling. This means for the carousel use case, images will be loaded before they visible in the viewport. This means the image loading is less likely to be noticeable to the user, but at the cost of more downloads. Use the Horizontal Lazy Loading demo to compare behaviour in Chrome versus Safari and Firefox.

What if I'm already using a third-party library or a script to lazy-load images?

With full support of lazy loading built into modern browsers, you probably don't need a third-party library or script to lazy-load images.

One reason to continue to use a third-party library alongside loading="lazy" is to provide a polyfill for browsers that don't support the attribute, or to have more control over when lazy loading is triggered.

How do I handle browsers that don't support lazy loading?

Browser-level image lazy loading is well supported across all the major browsers and is recommended for most use cases, to remove the need for extra dependencies on JavaScript.

However, if you have a need to support more browsers or want to have more control over lazy-loading thresholds then you can use a third-party library to lazy-load images on your site.

You can use the loading property to detect whether a browser supports the feature:

if ('loading' in HTMLImageElement.prototype) {
  // supported in browser
} else {
  // fetch polyfill/third-party library
}

For example, lazysizes is a popular JavaScript lazy loading library. You can detect support for the loading attribute to load lazysizes as a fallback library only when loading isn't supported. This works as follows:

  • Replace <img src> with <img data-src> to avoid an eager load in unsupported browsers. If the loading attribute is supported, swap data-src for src.
  • If loading isn't supported, load a fallback from lazysizes and initiate it, using the lazyload class to indicate which images to lazy-load:
<!-- Let's load this in-viewport image normally -->
<img src="hero.jpg" alt="…">

<!-- Let's lazy-load the rest of these images -->
<img data-src="unicorn.jpg" alt="…" loading="lazy" class="lazyload">
<img data-src="cats.jpg" alt="…" loading="lazy" class="lazyload">
<img data-src="dogs.jpg" alt="…" loading="lazy" class="lazyload">

<script>
  if ('loading' in HTMLImageElement.prototype) {
    const images = document.querySelectorAll('img[loading="lazy"]');
    images.forEach(img => {
      img.src = img.dataset.src;
    });
  } else {
    // Dynamically import the LazySizes library
    const script = document.createElement('script');
    script.src =
      'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js';
    document.body.appendChild(script);
  }
</script>

Here's a demo of this pattern. Try it in an older browser to see the fallback in action.

Is lazy loading for iframes also supported in browsers?

Browser Support

  • Chrome: 77.
  • Edge: 79.
  • Firefox: 121.
  • Safari: 16.4.

<iframe loading=lazy> has also been standardized. This lets you lazy-load iframes using the loading attribute. For more information, see It's time to lazy-load offscreen iframes!

How does browser-level lazy loading affect advertisements on a web page?

All ads displayed to the user as images or iframes lazy-load just like any other image or iframe.

How are images handled when a web page is printed?

All images and iframes load immediately when the page is printed. See issue #875403 for details.

Does Lighthouse recognize browser-level lazy loading?

Lighthouse 6.0 and higher factor in approaches for offscreen image lazy loading that can use different thresholds, letting them pass the Defer offscreen images audit.

Lazy-load images to improve performance

Browser support for lazy loading images can make it significantly easier for you to improve your pages' performance.

Are you noticing any unusual behavior with this feature enabled in Chrome? File a bug!