Lazy Loading Images, the performant way

Akash Verma
3 min readOct 5, 2020

In the fast-paced development cycle, performance optimizations often take a back seat and we lose out on the low hanging optimizations which could potentially improve user experiences and site load time. Most of the frontend developers work with images on a daily basis but often tend to miss the opportunities to optimize the loading of images.

In this blog, we will be talking about such low hanging optimizations that can be applied while working with images.

The built-in way

Browser-level support for lazy-loading images is now supported on the web. In Chrome 76 onwards, 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 are the supported values for the loading attribute in chromium base browser:

  • auto: Default lazy-loading behavior of the browser, which is the same as not including the attribute.
  • lazy: Defer loading of the resource until it reaches a threshold distance from the viewport.
  • eager: Load the resource immediately, regardless of where it's located on the page.

The cross-browser support is still not great and you can check on the compatibility here. Browsers not supporting loading attribute will simply ignore it and download the image.

Intersection Observer API

The intersection observer API gives you the capability to download images when it enters the visible viewport, thus preventing unnecessary downloads(network requests) if the user doesn’t scroll down to the image. You might have observed the behavior while navigating through the Medium web app where images are downloaded as you scroll through the content.

Let’s deep dive into the API to see how we can leverage and use it depending on the use-case. To start off, you need to create an instance of an Intersection Observer. The constructor takes in two parameters:

  • callback: The function that gets executed when the target element enters and leaves the visible viewport.
  • options: This lets you customize when the callback function will be triggered. You can check the available options in detail here.
let options = {
threshold: 0.2 // callback gets triggered when 0.2x of target enters viewport
}

let observer = new IntersectionObserver(callback, options);

The next step is to use the observer instance to observe the position of the target element.

let target = document.querySelector('#target');
observer.observe(target);

When 20% of the target element enters the visible viewport, the callback function is triggered with entries(list of observed targets) as the first argument and an instance of the observer object as second. Each entry in the list describes an intersection change for one observed target. Let’s talk about two of the attributes which we are interested in with respect to images.

  • isIntersecting: The boolean value conveys whether a target has entered visible viewport.
  • intersectionRatio: The value conveys the percentage of the target that entered the viewport when the callback function triggered.

Since we understand the API, let’s see how to use it to lazily load images.

<img data-src='url' />function callback(entries, imageObserver){
entries.forEach(entry => {
if(!entry.isIntersecting){
return;
}
const src = entry.target.getAttribute('data-src'); // get image url
entry.target.src = src; // insert src attribute to image
imageObserver.unobserver(entry.target);
});
}

In the above code snippet when the target image enters visible viewport we add src property to the image that triggers the download (only when it entered the viewport)and removes it from the list of observables as we no longer need it. You can check the browser support of the API here and add polyfill if required.

If you liked reading this, don’t forget to clap. 👏👏

You can also follow me on twitter @Akash940 for JavaScript or React updates.

Thank-you!

--

--

Akash Verma

JavaScript Enthusiast, Software Engineer @goevive. Follow me on twitter @Akash940