How to lazy load images with Intersection Observer

The majority of software engineers today would love to focus on the latest and greatest framework, technology, or library. However, not many of these engineers would care to look at some basics of the trade that can immediately improve the loading time of web apps. One such technique is ‘image lazy load’ levering lazy loading images, which happens when the images are outside the viewport. You can read more about lazy loading images here.

The Internet is full of ways to improve the performance of your web applications, but the most popular ones are CDN, image compression, and lazy loading.

In this blog, we will talk about tackling lazy load images with Intersection Observer API. By leveraging lazy loading images, we managed to save around 200% data and reduce the app loading time to 68%.

Image lazy load with Intersection Observer

What is image lazy load?

Usually in <img> when we type in the URL, it automatically downloads images even if it is outside the viewport. By using Intersection Observer we can download and render images on demand when the image DOM intersects with the viewport.

What is 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, which was introduced in 2017

Why did we choose Intersection Observer?

Intersection Observer is the more popular method for visibility detection over more traditional methods, such as onScroll + getBoundingClientRect() because the actual detection implementation doesn’t run on the main thread. However, the callback for when an intersection has been triggered does run on the main thread so remember to keep it light!

For context, I’m going to share with you the problem we faced with Freshchat and how we fixed it using Intersection Observer. Before delving into the details, I would first like to share the results of our fix. With Optimization Observer, we saved around 67% of data that was not in viewport. As a result of the data reduction, we managed to reduce DOM manipulation, and decrease the loading time of the app to 68%.

When user log in, they land on the Inbox page, which contains a list of conversations with visitor images. Every conversation has agents and visitors with profile images who are involved in this conversation, and also messages containing images. By using Intersection Observer, images are lazy loaded to improve the performance of the app.

How can you implement it?

It’s simple. Just follow these steps:


 Add the image url to data-src instead of src in <img> 

Note: If we add it directly to src, the browser automatically starts the download.  

In Javascript,

Create a new instance of Intersection Observer with the callback functionobserve’. Ask an observer to observe that image element.

Notice when an intersection was triggered between the root and target elements. In our case, the elements were the top-level document’s viewport and img.lazy ref respectively.

Observe function

Observer Instance creation

Before image lazy load

We downloaded all the images from all 40 conversations, visitor images, and selected conversations with images even those that were not in viewport. The total data we have downloaded is 1.1MB of image resources and also the time taken to render was 3.45s.

After image lazy load

The total data we have downloaded was 373KB of image resources and also time taken to render was 2.31s. Around 200% of bandwidth usage was saved, reducing the loading time of the app to 68%.


Using Intersection Observer API, we saved around ~740KB of data, ~1.14s of rendering time. We successfully reduced LCP to ~2.9s and interactive and blocking time. Around 200% of bandwidth usage was saved and the loading time of the app was brought down to 68%.

This is a better approach than the traditional way of dealing with lazy load becauseand I reiteratethe actual detection implementation doesn’t run on the main thread.

Cover image: Vignesh Rajan.