How to asynchronously load and decode images

Ifeoma Imoh

Images are an increasingly important part of the web. The ability to retain visual information is one of the many reasons we find images appealing, and this makes images more effective at conveying ideas across cultures. Imagine a website without images? Boring! However, if images aren't properly optimized, they can cause a lot of performance issues (such as taking up the loading resources), which can negatively impact the user experience on your website.

Before an image is displayed in the browser, it has to be loaded from the server first. Next, the browser has to read the data and convert it into something that the browser can display — a process known as decoding. After that, the result is painted on the screen. If you're looking to get the most out of your images, this post will discuss how to asynchronously load and decode images on your website to improve page load performance and user experience.

Here is a link to the demo on CodeSandbox.

Native Image Lazy-Loading

The browser will attempt to load everything on a web page at once, and as a result, the page takes longer to load if it contains lots of images because, as explained, images take time to process. With lazy loading, you can prioritize the important images in the user's initial viewport. There are different JavaScript-based solutions for lazy loading images, but in this post, we will look at how to lazy-load images using the loading attribute of the <img> element.

Lazy loading is a web performance technique that delays loading images that are offscreen until they are close to the user’s viewport, rather than loading them upfront (immediately the page loads). With modern browsers (Chrome, Edge, Firefox) supporting this feature, all you need to do to lazy load an image is add a loading attribute to an <img> element and set its value to lazy.

The supported values for the loading attribute are auto, eager, and lazy, where auto is the browser's default behavior. Setting the value of the loading attribute to lazy tells the browser to deprioritize loading the image when the page loads and fetch it as soon as possible or when the user is about to see them as they scroll.

Let’s take an example. Run these commands in your terminal to create an index.js file in a folder named async-load-decode-images.

1mkdir async-load-decode-images
2 cd async-load-decode-images
3 touch index.html

Create a folder called images at the root of your project and add six images in jpeg file format to it. The images should be named image1, image2, and so on until the sixth. You can get free images from Unsplash.

After that, add the following to your index.js file:

1<!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 <title>Lazy Loading Images</title>
8 <link rel="stylesheet" href="styles.css" />
9 </head>
10 <body>
11 <header>
12 <h1>Lazy Loading Images</h1>
13 </header>
14 <main>
15 <section>
16 <div>
17 <img
18 loading="eager"
19 src="./images/image1.jpeg"
20 alt="Image from https://unsplash.com/"
21 />
22 <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker, including versions of Lorem Ipsum</p>
23 </div>
24 <div>
25 <img
26 loading="eager"
27 src="./images/image2.jpeg"
28 alt="Image from https://unsplash.com/"
29 />
30 <p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source.</p>
31 </div>
32 <div>
33 <img
34 loading="eager"
35 src="./images/image3.jpeg"
36 alt="Image from https://unsplash.com/"
37 />
38 <p>There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet.</p>
39 </div>
40 </section>
41 <section>
42 <div>
43 <img
44 loading="lazy"
45 src="./images/image4.jpeg"
46 alt="Image from https://unsplash.com/"
47 />
48 <p>Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 </p>
49 </div>
50 <div>
51 <img
52 loading="lazy"
53 src="./images/image5.jpeg"
54 alt="Image from https://unsplash.com/"
55 />
56 <p>It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source.</p>
57 </div>
58 <div>
59 <img
60 loading="lazy"
61 src="./images/image6.jpeg"
62 alt="Image from https://unsplash.com/"
63 />
64 <p>The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
65 </div>
66 </section>
67 </main>
68 </body>
69 </html>

In the code above, we have six <img> tags with specified width and height attributes. We set the value of the loading attribute in the first three img tags to eager so that they can be loaded immediately because when the page loads, they are in the first visible viewport. The loading attribute in the last three images is set to lazy because they are below the viewport, so we don’t want them to be loaded on page load until the user approaches them.

If you open this page in your browser (I’m using Chrome), you should see all six images. Now to open the Developer Tools on that page, right click, click Inspect then, then navigate to Network and reload the page.

The Network tab in the gif above shows that the first three images are loaded when you reload the page. Then as I scroll the page, the other images get loaded. When we use the Chrome network throttler in the dev tools to simulate a slower network connection (slow 3G), the first three images are loaded first, followed by the last three images, which are lazy-loaded.

Notice how we didn't need to scroll down the page even on a slow network, and the last three images were fully loaded. This is because significant improvements (new distance-from-viewport thresholds) were made to <img> lazy-loading in Chrome, as lazy-loaded images may not be fully loaded before entering the browser's viewport. These improvements allow offscreen images that are nearby to be loaded early, ensuring that they have finished loading and are visible by the time a user scrolls to them.

Note that the size of an image can also affect the time it takes to load the image. Even if the loading attribute is set to eager, larger images take longer to load. You can explore that by adding large images and checking the network tab in the Dev Tools.

HTML <img> Decoding Attribute

As explained earlier, before an image is displayed in the browser, it has to be loaded and decoded. The problem is that decoding is an expensive process, and because JavaScript is single-threaded, decoding happens synchronously on the main thread.

Because the browser loads all the resources (text, images) on a web page synchronously before rendering the page, it would take longer to decode heavy or multiple images on a page. This would delay the rendering of the page, causing the UI to freeze until it is complete.

Several factors (image size, format, etc.) contribute to the time it takes to display an image. Still, the time it takes to decode an image is very important in determining when to load the image once it has been downloaded.

One way to control image decoding and improve image load times is to use the decoding attribute of the HTML <img> element and set it's value to async like so:

1<img
2 loading="lazy"
3 src="./images/image6.jpeg"
4 alt="Image from https://unsplash.com/"
5 width="400" height="400"
6 decoding="async"
7 />

This way, image decoding happens asynchronously (off the main thread), leaving the main thread free so the browser can continue rendering the other content of the page. It defers the image decoding until later and displays it when it is done.

The decoding attribute is supported by all major browsers (Chrome, Edge, and Firefox), and it accepts three values:

auto: up to the browser to decide between sync or async decoding. sync: images and other page resources are rendered at the same time on the main thread. async: tells the browser to continue rendering other page content and display the image when the decoding is complete. This makes rendering faster and is preferred for performance.

You can find the project here on Github.

Conclusion

This post explored how to asynchronously load and decode images to significantly improve page load performance, save cost (data) and improve user experience.

Resources you may find useful:

Ifeoma Imoh

Software Developer

Ifeoma is a software developer and technical content creator in love with all things JavaScript.