I recently made some performance improvements to the DebugBear homepage. This article explains how to use the loading="lazy"
HTML attribute to make your website render more quickly.
The problem: resource prioritization
The filmstrip above shows that the background image of the hero section only loads after about 1.6s. Before that a plain blue background is used.
Why does it take so long? We can find out by looking at the detailed request waterfall.
The request for the background image is only made relatively late. Before that the browser loads several large below-the fold images, using up the available bandwidth.
The right-most part of each request shows the response body being downloaded. The darker areas show when data for that request is received, so you can see what requests the browser is spending bandwidth on.
Most of this is allocated to the below-the-fold images, but the browser is also loading some JavaScript code before loading the hero background image.
What does the HTML loading="lazy" attribute do?
By default, all images on a page are loaded as soon as the user opens the page. Setting the loading
attribute to lazy
can defer fetching images and iframes until the user scrolls near the element.
<img src="image.png" loading="lazy" />
That avoid unnecessary downloads and helps the browser focus on the most important requests.
Impact of implementing the solution
The below-the-fold images are now deprioritized, and the browser loads the background image much earlier. The Largest Contentful Paint goes from 1.6s to 0.8s.
(The filmstrip makes it look like the above-the-fold image loads later than before, but this is only minor variation, not an effect of the lazy loading change.)
What browsers support the loading="lazy" attribute?
Native lazy loading is supported across all major browsers, like Chrome, Firefox, Safari, and Edge.
Why is native image lazy loading better than using a library?
Using a library to lazy load images means that you first need to load JavaScript code before starting to load the image. If the browser starts scrolling down it may take a while before images show up.
This is especially noticeable if the LCP image is lazy loaded. In this waterfall you can see a long request chain for the LCP image.
Request waterfall with lazy loaded LCP image
How do you lazy load a background image?
The loading=lazy
attribute doesn't work for background images, so you'll need to use JavaScript to achieve the same effect. You can check if the image is near the viewport and then either modify the style
attribute directly or add a class with a background-image
defined in a CSS file.
You can use the Intersection Observer API to check whether the image is in the viewport.
What if my user loads an article and then wants to read it offline?
If you're writing a long blog post readers might prefer to load it on their phone and then read it when they don't have an internet connection.
When images are lazy loaded they'll be missing once the reader scrolls down the page. To avoid that you can remove the loading
attribute from the img
tags after the initial page load.
setTimeout(() => {
document
.querySelectorAll("img[loading='lazy']")
.forEach((el) => el.setAttribute("loading", ""));
}, 5000);
The images will then be downloaded right away, but without having affected the initial page load.
The trade-off here is bandwidth consumption: on a mobile plan with limited traffic users may prefer not loading images that they aren't going to see.
A worse Lighthouse Performance score
One thing that happened when I enabled image lazy loading and later removed the loading
attribute is that my Lighthouse Performance score went down.
The initial render was a lot quicker, but Time to Interactive went up because more network activity was moved to later in the page load process, keeping the network active for longer.
Is this a problem? Not really. I care about user experience and the Core Web Vitals Google uses for search engine rankings. Time to Interactive doesn't matter in either case.
One easy workaround would be to increase the delay from 5s to 10s. Lighthouse assumes the network is idle after 5s of no activity, so pushing back the extra image requests will improve Time to Interactive.
Summary
You can use image lazy loading to prioritize more important requests and speed up the initial rendering of your page.
The Lighthouse Performance score isn't the most important metrics to optimize for, and sometimes it's ok to make a performance optimization that reduces the Performance score.
Want to try lazy loading on your website? The DebugBear Experiments feature lets you run performance tests on your website without having to deploy any changes.
Try DebugBear for free to monitor and optimize your site speed.