loading...
Cover image for How to Lazy Load Images

How to Lazy Load Images

shandilyaprasanna profile image Prasanna Shandilya ・4 min read

What is lazy loading images?

Lazy loading is a technique that defers the loading of non-critical resources at page load time. Instead, these resources are loaded when required. Where images are concerned, "non-critical" is often synonymous with "below the fold" images
Lazy Loading defers the loading of an image that is not needed on the page immediately. An image, not visible to the user when the page loads, is loaded later when the user scrolls and the image actually become visible. If the user never scrolls, an image that is not visible to the user never gets loaded.

It carries two main advantages.

1. Performance Improvement

With lazy loading, we are reducing the number of images that need to be loaded on the page initially. Lesser resource requests mean lesser bytes to download and lesser competition for the limited network bandwidth available to the user. This ensures that the device is able to download and process the remaining resources much faster. Hence, the page becomes usable much sooner as compared to one without lazy loading.

2. Cost reduction

The second benefit for us is in terms of delivery costs. Image delivery, or delivery of any other asset, is usually charged on the basis of the number of bytes transferred.

Lazy Load Images

1.)Using Intersection Observer

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport.

It lets you register a callback function that is executed whenever an element you wish to monitor enters or exits another element (or the viewport).

You need to pass option object into Intersection Observer constructor, It has following fields-

root:-

The element that is used as the viewport for checking visibility of the target. Defaults to the viewport.

rootMargin:-

Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages.

threshold:-

It can take either a single number or an array of numbers which indicate at what percentage you want to detect and fire callback. If you only want to detect when visibility passes the 50% mark, you can use a value of 0.5. If you pass an array like [0, 0.25, 0.5, 0.75, 1] then callback will run every time visibility passes another 25%. The default is 0 (meaning as soon as even one pixel is visible, the callback will execute).
intersection Observer Code

2.)Using event handlers (Most compatible)

Using event listener on scroll event and every time user scroll to end of the page we can fire the fetch call for more images.
This method works for all the browser.
Note:-Use intersection observer wherever possible, and fall back to event handlers if the widest possible compatibility is a critical application requirement.

3.)Native lazy-loading for the web

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

In Chrome 76, you can use the loading attribute to completely defer the loading of offscreen images and iframes that can be reached by scrolling:
If lazy load is not enabled in your browser you can enable it here:- chrome://flags/

<img src="image.png" loading="lazy" alt="…" width="200" height="200">
<iframe src="https://example.com" loading="lazy"></iframe>

Here are the supported values for the loading attribute:

->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 calculated distance from the viewport.
->eager: Load the resource immediately, regardless of where it's located on the page.
Native code

Demo Time:-

This demo shows difference between different approaches discussed above

First radio button is for Intersection Observer, You can see based on rootMargin only three image request is made on load,

Native code

Second is for Native chrome support in this case, out of 100 images on load 20 images are fetched

Native code

The third one is the default case, in this case, we can see all 100 images are downloaded, even though the user might not scroll that far

Native code

Posted on by:

shandilyaprasanna profile

Prasanna Shandilya

@shandilyaprasanna

I am a JS developer with industry experience in OTT and E-commerce domain building websites, web applications and PWA. I specialize in JavaScript with experience in react, node.

Discussion

markdown guide
 

I added lazy loading to my Jekyll blog a couple weeks ago, and the results were immediately noticeable. Now most of my pages get a solid 100 on PageSpeed Insights.

I wrote a Python script, thumb, that lets me quickly generate scaled-down thumbnails of each image in my assets folder. These are about 1 KB each and serve as fuzzy/blurry placeholders. I then use the IntersectionObserver API to replace the blurry images with the actual image.

On top of all that, I'm also using WebP with fallbacks and generating WebP copies for all of my images in one go with another Python script, webp, that basically just wraps around the cwebp and gif2webp CLI tools that Google ships.

 

Thanks for sharing your experience.

 

Great intro-post for us that's never implemented lazy loading.
For the future it would be best of you did something similar for loading thumbnails/compressed/blurred images before real ones for performance.

 
 

lazysizes is a good well-documented library, I have personally never used it.
there are other library options as well:-
->blazy
->lozad.js
->react-lazyload

The motive of this article was to show how lazy load works under the hood. Otherwise, you can always grab a library and call it a day.