Defer Offscreen Images

Delay loading images that appear below-the-fold for faster rendering of above-the-fold page content.

Last Updated : April 21, 2021


What it means?

Deferring off screen images means delaying loading of images that appear below-the-fold on the page. This ensures that above-the-fold content on the page renders faster. This how-to details how we can defer off-screen images in various situations:

Defer off-screen images on a regular website

If you have a complete control over the page's HTML code, you can use one of the two approaches detailed below to lazy-load the images:

With Native Lazy Loading (no JavaScript)

With native lazy loading, images loading can be deferred without any JavaScript. This makes the implementation very simple. The downside of this approach (as of April, 2021) is that this won't defer loading of images on the Safari browser. To achieve native image lazy loading, add the attribute loading="lazy" to the img tag:

<!--Use loading="lazy" attribute for images that are to be loaded lazily -->
<img src="image.jpg" loading="lazy" />

With JavaScript

If you are looking for a lazy-loading solution that has a wider cross-browser support, you can use a lightweight JavaScript library to achieve this.

1. Download the lazysizes Javascript library from here. Once downloaded, include it in your page through the following script tag:

<script src="lazysizes.min.js" async=""></script>

2. Within your HTML <img> tags:

  • Add class="lazyload" attribute to all images
  • Change the src attribute to data-src

<!--Use data-src. And, specify lazyload class -->
<img data-src="image.jpg" class="lazyload" />

The above change will ensure that the loading of image.jpg is deferred in case if it is offscreen else it will be loaded immediately. So, the above change shall be applied to all the images on the page. Read on here if you want to use this library with responsive images or WebP images or both.

Note: To ensure that the deferred off-screen images are SEO friendly, check-out this how-to.

Defer off-screen images on a Wordpress website:

If you seek defer loading of off-screen images on your wordpress website, a3 Lazy Load is the Wordpress Plugin of our choice. The plugin allows you to setup lazy loading within a few clicks with SEO friendly no-script tags, custom loaders, capability to lazy load videos, feature to exclude specific images, etc. The plugin has 100k+ installations with a rating of 4.4

Setting up a3 Lazy Load Wordpress Plugin
Setting up a3 Lazy Load Wordpress Plugin

Defer offscreen images on a Magento site:

If you seek to setup deferred loading of offscreen images for your Magento site, we recommend WeltPixel's lazy loading extension. Once installed and enabled from Magento admin, this extension works out of the box with Magente 2 product pages, search pages and listing pages. The extension is SEO friendly and can be used with custom images within the store as well.

Setting up WeltPixel Lazy Loading Magento Extension
Setting up WeltPixel Lazy Loading Magento Extension

Defer offscreen images on Shopify Site:

To defer loading of offscreen images in your Shopify site, you will have to modify your Theme (theme.liquid file) to leverage the lazySizes library. Here's how:

1. Download the lazysizes Javascript library from here and upload it to your theme's /assets folder.

2. Add the following lines to your theme.liquid just before the </head> tag:

<script src="{{ 'lazysizes.min.js' | asset_url }}" async="async"></script>

3. Change the image tags in your theme by adding class "lazyload" to the image tags and changing the src attribute to data-src attribute:

{% for img in product.images %}
 {% assign img_url = img | img_url: '1x1' | replace: '_1x1.', '_{width}x.' %}
 <img class="lazyload" 
 src="{{ img | img_url: '300x' }}"
 data-src="{{ img_url }}"
 data-widths="[720, 1080]"
 data-aspectratio="{{ img.aspect_ratio }}"
 data-sizes="auto">
{% endfor %}

Defer offscreen images on a React website:

There are two ways to defer images for a React based website. An easier implementation can be achieved by using an npm package. But, this would add a few KBs to your React bundle size. Avoiding this would require creating your own implementation to lazy-load images.

With an npm package

react-lazy-load-image-component is our recommended package (npm, github). At 5 kB gzipped & minified, this is a lightweight solution to achieve the desired lazy loading. It provides you with a LazyLoadImage component that you can used instead of the regular image component. This ensures that the image file is downloaded only when the component is in the user's viewport:

import { LazyLoadImage } from 'react-lazy-load-image-component'
.
.
.
const MyComponent = () => {
  ....
  ....
  return (
   <LazyLoadImage
          alt="provide your alt text here"
          height="specify image height"
          src="specify img src path here"
          width="specify image width" />
  );
}

The component also comes with a bunch of other options to execute code before or after rendering component, have a placeholder element for the image, provide a blur-effect when loading the image, etc.

Without an external package

We can leverage the browser's IntersectionObserver to create an image lazy-loading solution of our own. Below is a light-weight implementation to lazy-load images.

1. Define the image lazy-loading function in a file (like util.js)

export const lazyImage = function () {
   if ("IntersectionObserver" in window) {
   const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.intersectionRatio > 0) {
          if (entry.target.hasAttribute("data-src")) {
            entry.target.setAttribute(
              "src",
              entry.target.getAttribute("data-src")
            );
            observer.unobserve(entry.target);
          }
        }
      });
    });
    document.querySelectorAll(".lazy-image").forEach((gameImg) => {
      if (gameImg.getAttribute("is-observed") != "true" && gameImg.getAttribute("data-src") != null)
      {
        gameImg.setAttribute("is-observed", "true")
        observer.observe(gameImg);  
      }
    });
  } else {
    var imgList = document.querySelectorAll(".lazy-image");
    Array.prototype.forEach.call(imgList, function (image) {
      image.setAttribute("src", image.getAttribute("data-src"));
    });
  }
};

2. To lazy-load images within any component:

  • Instead of setting the src to the image URL, set the data-src to the image URL.
  • Set the src to a blank inline image.
  • Add the class lazy-image to the img tag's className.
import React, { useEffect } from "react";
import { lazyImage } from "common/util";
export const MyComponent=(props)=>{
    useEffect(() => {
        lazyImage();
      });
    return (
      <div className="container">
        <img data-src="specify-img-src-path-here" 
        className="lazy-image" 
        src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" />
      </div>
    );
}



Punit Sethi

About the Author

Punit Sethi has been working with large e-Commerce & B2C websites on improving their site speed, scalability and frontend architecture. He tweets on these topics here.

Also Read