There are a few different “traditional” ways of lazy loading of images. They all require JavaScript needing to figure out if an image is currently visible within the browser’s viewport or not. Traditional approaches might be:
- Listening to
scroll
andresize
events on thewindow
- Using a timer like
setInterval
Both of these have performance problems.
If you’re looking to add lazy loading to your site’s media right now, Lozad.js isn’t a bad choice, but native support is going to start happening soon so that should be considered in your markup plans.
Why traditional approaches are not performant?
Both of those approaches listed above are problematic because they work repeatedly and their function triggers **forced layout while calculating the position of the element with respect to the viewport, to check if the element is inside the viewport or not.
To combat these performance problems, some libraries throttle the function calls that do these things, limiting the number of times they are done.
Even then, repeated layout/reflow triggering operations consume precious time while a user interacts with the site and induces “junk” (that sluggish feeling when interacting with a site that nobody likes).
There is another approach we could use, that makes use of a new browser API designed specifically to help us with things like lazy loading: the Intersection Observer API.
That’s exactly what my own library, Lozad.js, uses.

What makes Lozad.js performant?
Intersection Observers are the main ingredient. They allow registration of callback functions which get called when a monitored element enters or exits another element (or the viewport itself).
While Intersection Observers don’t provide the exact pixels which overlap, they allow listening to events that allow us to watch if elements enter other elements by X% (configurable), then the callback gets fired. That is exactly our use case when using Intersection Observers for lazy loading.
Quick facts about Lozad.js
- Light-weight: just 535 bytes minified & gzipped
- No dependencies
- Uses the IntersectionObserver API
- Allows lazy loading of dynamically added elements as well (not just images), though a custom load function
Usage
Install from npm
:
yarn add lozad
or via CDN:
<script src="https://cdn.jsdelivr.net/npm/lozad"></script>
In your HTML, add a class to any image you wish to lazy load. The class can be changed via configuration, but “lozad” is the default.
<img class="lozad" data-src="image.png">
Also note we’ve removed the src
attribute of the image and replaced it with data-src
. This prevents the image from being loaded before the JavaScript executes and determines it should be. It’s up to you to consider the implications there. With this HTML, images won’t be shown at all until JavaScript executes. Nor will they be shown in contexts like RSS or other syndication. You may want to filter your HTML to only use this markup pattern when shown on your own website, and not elsewhere.
In JavaScript, initialize Lozad library with the options:
const observer = lozad(); // lazy loads elements with default selector as ".lozad"
observer.observe();
Read here about the complete list of options available in Lozad.js API.
Demo
See the Pen oGgxJr by Apoorv Saxena (@ApoorvSaxena) on CodePen.
Browser support
Browser support is limited, as the feature is relatively new. Use the official IntersectionObserver polyfill to overcome the limited support of this API.
“Browser support is limited, as the feature is relatively new. Use the official IntersectionObserver polyfill to overcome the limited support of this API.”
Does this mean we have to implement it ourselves, or does lozad automatically do it?
It sounds like it’s something we have to do ourselves, but if the browser support is limited, then I think many people will desire it. Maybe you could include it in a “fat-lozad” version?
That’s a good suggestion, though it’d be advised to load the polyfill from polyfill.io so that it’s loaded only in browsers which don’t support IntersectionObserver API, and that way browsers supporting the feature already, won’t have to load additional code.
I really like this idea, but how would you implement this in say WordPress or some other CMS?
You’d have to add some other magic in to manipulate images that should be showing “now”, but later should be lazy-loaded.
When I first saw the intersection API, this is the exact use I envisioned. Just need a couple more pieces to the puzzle to finish this off.
Can you remove all “src” attribute values on some async/defer trick on load in JS? I haven’t tried this, but it may work… But it seems hackish (which I am generally opposed to…)
I made a crude WordPress plugin which uses an adapted version of Lozad.js to lazyload images. It misses some settings but it works, has a alternative for when JS is disabled and you can choose to add the polyfill in the back-end. You can find the plugin here https://github.com/aderaaij/wp-image-preload
Does it work for background images loaded thru background-image ?
Yes, it works for lazy loading background images as well, add
data-background-image
attribute to the element to be lazy loaded as mentioned in the usage.Interesting method of lazy loading images. You might want to be careful with the Lotad image in your banner, though. Nintendo hasn’t been too kind recently to developers using their intellectual property.
Sure, I’ll get that fixed soon.
Thank you for that script! I hope to see it soon implemented as WordPress plugin too! :)
It doesn’t look like it is compatible with the picture element?
Lozad.js supports lazy loading of responsive images, do open an issue on Github if there’s a specific case that you want to highlight.
…with the site and induces “junk” (that sluggish feeling…
I think it should be “jank”, not “junk”.
http://jankfree.org/
It baffles me that so few lazy loaders have a no-js fallback and support for the picture element and srcset. Here’s the best one I know of that does (doesn’t user Intersection Observer yet): https://github.com/tvler/lazy-progressive-enhancement
Lozad.js FAQs mentions the no-js fallback. Also, it does support lazy loading of responsive images, read here.
Awesome!! Sorry, I did not mean to imply that your solution does not have that support, I was venting in response to another comment about
<picture>
. Thanks for the reply and for the plugin, I’m looking forward to trying it out.I agree. A WordPress plugin with an option to include the polyfill would be great!
This is good stuff. I added
IntersectionObserver
support to my own lazy loading script (https://github.com/malchata/yall.js) not long ago, but I still fall back to throttled scroll event listeners. I’m glad people are usingIntersectionObserver
to accomplish this, because it really helps to cut down on jank. Keep up the good work, dude.How does this work with SEO support, will google see the lazy loaded images?
FAQs answers your question.
Hey, that is great stuff. I would love to see this work in IE9. Even with the Polifills it does not work. Your Demo also does not work: https://apoorv.pro/lozad.js/demo/
Would be great to have a solution here!
Thanks,
Sebastian
Please open an issue on Github, and it will be prioritised.
Apoorv, I love the library. However, I cannot abide the Papyrus in your logo. Seriously, I’ll even do a logo for you for free.
I was thinking the same thing
Thanks for extending your help Michael, will surely contact you for designing logo for Lozad.js 2.0 :)
Awesome Apoorv, I’m totally serious. I’d love to help
Just to let you guys know. I am a friend of performance and do not see any benefits (at least on my project) to have this in old ie browsers. here is the code:
Would Lozad work with React and virtual dom?
I found this on npm, but it appears to be an empty repo https://www.npmjs.com/package/react-lozad
Currently trying to resolve an issue with too many requests on page load and wanted to utilize your script, however with the nature of react, it doesn’t seem to work.
Any suggestions?
Thanks!
Yes, it works with React without any other module usage. Checkout this https://codepen.io/PezCoder/pen/mBaOer
What if the src is broken? I get the data-src from the back-end and have a placeholer in src, but the placeholder gets replaced even if the link from back-end doesn’t exist. How do I prevent Lozad from replacing the placeholder in src with the url in data-src when there is no url?
PS. In dev tools I get src(unknown) data-loaded=”true”.
Image slider using Lozad from Travis McGeehan: