Logo Homepage

Optimize Your Web App - Images Performance

We love high-resolution images. A clear and vivid image makes a website visually more appealing, leaves a stronger impression on customers, and helps to better convey personal insight or business vision.

However, according to HTTP Archive: Page Weight, image is also one of the leading causes of web performance issues, with a staggering average page weight of ~1000 KB.

Average page weight of images (2020 - 2024)

This number is quite alerting, given that it far exceeds the size of HTML, CSS and JavaScript combined, and is still ever-growing. And as we all know, a heavier page weight usually means a slower loading time, and consequently a lower user retention.

Average total page weight (2020 - 2024)

As a web developer, we are thus confronted with a dilemma - we want to deliver high-quality images to our customers; but meanwhile, we also want to reduce the page weight.

Luckily, there are numerous ways to optimize our images. Let’s take a look!

🔽 Reduce Image Quality Undetected

Brute force solutions are always simple and intuitive. The less information an image contains, the easier it is to be compressed, thus the better the performance is.

These steps applied, the size of an image could well be reduced to ~3% of the original, with humans barely noticing the difference.

My wallpaper is compressed to 0.6% of original size by Sharp

🎞️ Choose the Right Format

If you have tried out Squoosh before, you know what I am saying. Simply convert a PNG file to WEBP, and the image size could shrink to ~15%.

Yes, WEBP is super awesome, but it is not always the go-to choice. I list some considerations below.

I feel like compression and converting tools should always be integrated into the build process, which is something Next.js and Astro are already doing.

Or we could even take a step further to offer compression and conversion in a dynamic and programmatic way, e.g. the Unsplash image API, which, given different query parameters “w” and “h”, returns images of different sizes.

🚀 Faster Delivery over Network

Now that there is not much left to do with the original images, let’s shift our focus onto the network.

HTTP/2.0 is widely available across major browsers

🖼️ Responsive Images

And that’s pretty much I can think of on network perspective. Let’s now go and check what we can do with the actual code.

Suppose we offer a 1920 pixels wide image. For laptops, it works like a charm; but for mobile devices, they also receive that same 1920 pixels wide image, which is obviously out of their bounds and unnecessary.

“srcset” is a native attribute of <img>, which allows us to provide images of different sizes for devices of different widths. The “size” attribute could then be utilized to run media query based on these images.

"srcset" and "size" attribute of a responsive image

Typically, 3-4 alternative images would suffice. Going too far is as bad as not going far enough.

🛏️ Lazy Loading

Well… This could also be a brute force way of thinking. If the user can’t see it, then just don’t load it. And yes, lazy loading is all about delaying the loading of images, until the user really scrolls near them.

Native Attribute

The <img> element provides a “loading” attribute out of the box, which defaults to “eager”, but could also be set to “lazy” for native lazy loading. It’s neat, clean and efficient - just type a few letters, and you get a huge performance boost.

loading="lazy"

By the way, many people literally know nothing about it, which confuses me quite a lot. Many interviewees are completely at a loss when I ask them about it, and most of them would instead say they implemented lazy loading by listening to scroll events. Please… don’t do this unless you are smarter than the browser, and you know how to debounce, throttle, etc.

Also, it is worth noting that lazy loading is not a perfect fit for LCP. It would instead hurt the performance.

Intersection Observer

If you want to gain more granular control over lazy loading, you may want to use IntersectionObserver, a native browser API. (Please don’t imperatively listen to scroll events again!)

This is often used for use cases like infinite scrolling, where a user would scroll all the way down to the bottom, and a new batch of images are automatically fetched and shown.

📌 Conclusion

I have supposed several ways to optimize the performance of images on web applications, ranging from tweaking the image itself, to network optimization, and also explicit code control.

If I miss out anything important, please do let me know in the comment section. Any idea or suggestion is appreciated!