Images are critical. Whether it is marketing banners, product images, or logos, it is impossible to imagine a website without images. Sadly though, images are often heavy files making them the single biggest contributor to the page bloat. According to the HTTP Archive’s State of Images report, the median page size on desktops is 1511 KB and images account for nearly 45% (650 KB) of that total.
That said, it’s not like we can simply do away with images. They’re too important to the overall user experience. Instead, we need to make our web pages load really fast with them. In this guide, we will cover all of the ins and outs of lazy loading images, a technique that helps improve the time it takes for a web page to load by deferring image loads until they are needed.
This post does a great job of covering the subject of lazy loading in detail and all the considerations, tooling, tech, etc. But it was written just before native lazy loading start becoming a thing, so when figuring out your solution to implement, you’d probably be smart to incorporate native lazy loading into it:
Before we dive right in, here is a sample video that demonstrates the concept. In short, a gray placeholder box is rendered on the page until it scrolls into view—at which point the actual image loads in place of the box.
Chapter 1: What is Lazy Loading?
We often associate the word “lazy” with avoiding work as long as possible, or the sheer act of wanting to do nothing at all.
Similarly, lazy loading defers the loading of resources on the page as long as they are not needed. Instead of loading them right away, which is what normally happens, we allow them to load later.
Lazy Loading is a set of techniques in web and application development that defers the loading of resources on a page to a later point in time—when those resources are actually needed instead of loading them up front. These techniques help in improving performance, better utilization of the device’s resources and reducing associated costs.
The technique of lazy loading can be applied to just about any resources on a page. For example, even a JavaScript file can be held back if it is best not to load it initially. Same deal for an image—load it when we need it.
We will stick to lazy loading images in this guide, but it’s good to know it can be applied to other assets.
Chapter 2: Why Lazy Load at All?
If the user never scrolls to the point of the page that contains the image, then the user will never see that image. It also never loads in the first place because, hey, it was never needed.
You may already start to see how this benefits both you and the user. Here are two of the advantages we get with lazy loading.
Performance Gains
The obvious benefit is that we get smaller web pages that load faster. Lazy loading reduces the number of images that need to be loaded on a page upfront. Fewer image requests mean fewer bytes to download. And fewer bytes to download means the page renders faster than if those bytes and requests were being made.
This ensures that any device on any network is able to download and process the remaining resources much faster. Hence, the time from request to render becomes smaller and the page becomes usable much earlier. Win-win!
Cost reduction
The second benefit is for you as a website administrator. Cloud hosting services, like Content Delivery Networks (CDNs) or web servers or storages, deliver images (or any asset for that matter) at a cost based on the number of bytes transferred. A lazy-loaded image may never get loaded if the user never reaches it. Thus, you may reduce the total bytes delivered on the page and ultimately save yourself a few pennies in the process. This is especially true for users that instantly bounce off a page or interact only with the top portion of the content.
The reduction in bytes transferred from your delivery network or server reduces delivery costs. This will become more apparent as we explore lazy loading in the coming sections.
Just how much will you save? You can find out which images are a candidate for lazy loading and how many bytes you can save on the initial page load by using the Google Lighthouse audit tool. This has a section dedicated for offscreen images. You can also use ImageKit’s website analyzer to identify if your website uses lazy loading or not apart from other critical image-related optimizations on your page.
Lazy loading is critical not only to good performance but also to deliver a good user experience. Since combining performance and user experience with lazy loading is important and challenging, we will continue to address this topic in more detail throughout this guide after we have looked at different ways to lazy load images.
Chapter 3: Lazy Loading Techniques for Images
There are two common ways that we load images to a page: the <img>
tag and the CSS background-image
property. We will first look at the more common of the two, the <img>
tag and then move to CSS background images.
Lazy loading images in an image tag
Let’s start with the typical HTML markup for an image:
<img src="/path/to/some/image.jpg">
The markup for lazy loading images is pretty similar.
Step one is to prevent the image load upfront. The browser uses the src
attribute of the tag to trigger the image load. It doesn’t matter if it is the first or the 1,000th image in your HTML. If the browser gets the src
attribute, it will trigger the image to be downloaded, regardless of whether it is in or out of current view.
To defer the load, put the image URL in an attribute other than src
. Let’s say we specify the image URL in the data-src
attribute of the image tag. Now that src
is empty and the browser won’t trigger the image load:
<img data-src="https://ik.imagekit.io/demo/default-image.jpg">
Now that we’re preventing the image from loading, we need to tell the browser when to load it. Otherwise, it will never happen. For this, we check that as soon as the image (i.e. its placeholder) enters the viewport, we trigger the load.
There are two ways to check when an image enters the viewport. Let’s look at both of them with working code samples.
Method 1: Trigger the image load using Javascript events
This technique uses event listeners on the scroll
, resize
and orientationChange
events in the browser. The scroll event is pretty clear cut because it watches where the user is on a page as scrolling occurs. The resize
and orientationChange
events are equally important. The resize
event occurs when the browser window size changes, whereas orientationChange
gets triggered when the device is rotated from landscape to portrait, or vice versa.
We can use these three events to recognize a change in the screen and determine the number of images that become visible on the screen and trigger them to load accordingly.
When any of these events occur, we find all the images on the page that are deferred and, from these images, we check which ones are currently in the viewport. This is done using an image’s top offset, the current document top position, and window height. If an image has entered the viewport, we pick the URL from the data-src
attribute and move it to the src
attribute and the image will load as a result.
Note that we will ask JavaScript to select images that contain a lazy
class. Once the image has loaded, we’ll remove the class because it no longer needs to trigger an event. And, once all the images are loaded, we remove the event listeners as well.
When we scroll, the scroll event triggers multiple times rapidly. Thus, for performance, we are adding a small timeout to our script that throttles the lazy loading function execution so it doesn’t block other tasks running in the same thread in the browser.
Here is a working example of this approach.
Note that the first three images in this example are loaded up front. The URL is present directly in the src
attribute instead of the data-src
attribute. This is essential for a good user experience. Since these images are at the top of the page, they should be made visible as soon as possible. There’s no need to wait for JavaScript to load them.
Method 2: Trigger the image load using the Intersection Observer API
The Intersection Observer API is relatively new. It makes it simple to detect when an element enters the viewport and take an action when it does. In the previous method, we had to bind events, keep performance in mind and implement a way to calculate if the element was in the viewport or not. The Intersection Observer API removes all that overhead by avoiding the math and delivering great performance out of the box.
Below is an example using the API to lazy load images. We attach the observer on all the images to be lazy loaded. Once the API detects that the element has entered the viewport, using the isIntersecting
property, we pick the URL from the data-src
attribute and move it to the src
attribute for the browser to trigger the image load. Once this is done, we remove the lazy
class from the image and also remove the observer from that image.
If you compare the image loading times for the two methods (event listeners vs. Intersection Observer), you will find that images load much faster using the Intersection Observer API and that the action is triggered quicker as well— and yet the site doesn’t appear sluggish at all, even in the process of scrolling. In the method involving event listeners, we had to add a timeout to make it performant, which has a slightly negative impact on the user experience as the image load is triggered with a slight delay.
However, like any new feature, the support for Intersection Observer API is not available across all browsers.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
58 | 55 | No | 16 | 12.1 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
115 | 115 | 115 | 12.2-12.5 |
So, we need to fall back to the event listener method in browsers where the Intersection Observer API is not supported. We have taken this into account in the example above.
Chapter 4: Lazy Loading CSS Background Images
A common background image in CSS:
.my-class {
background-image: url('/path/to/some/image.jpg');
/* more styles */
}
CSS background images are not as straightforward as the image tag. To load them, the browser needs to build the DOM tree as well as the CSSOM tree to decide if the CSS style applies to a DOM node in the current document. If the CSS rule specifying the background image does not apply to an element in the document, then the browser does not load the background image. If the CSS rule is applicable to an element in the current document, then the browser loads the image.
Huh? This may seem complex at first, but this same behavior forms the basis of the technique for lazy loading background images. Simply put, we trick the browser into not applying the background-image
CSS property to an element, till that element comes into the viewport.
Here is a working example that lazy loads a CSS background image.
One thing to note here is that the JavaScript code for lazy loading is still the same. We are still using the Intersection Observer API method with a fallback to the event listeners. The “trick” lies in the CSS.
We have an element with ID bg-image
that has a background-image
. However, when we add the lazy
class to the element, we can override the background-image
property by setting the value of it to none
in the CSS.
Since an element with an ID and a class has higher specificity in CSS than an ID alone, the browser applies the property background-image: none
to the element initially. When we scroll down, the Intersection Observer API (or event listeners, depending on which method you choose) detects that the image is in the viewport, it removes the lazy
class from the element. This changes the applicable CSS and applies the actual background-image
property to the element, triggering the load of the background image.
Chapter 5: Creating a Better User Experience With Lazy Loading
Lazy loading presents a great performance benefit. For an e-commerce company that loads hundreds of product images on a page, lazy loading can provide a significant improvement in initial page loads while decreasing bandwidth consumption.
However, a lot of companies do not opt for lazy loading because they believe it goes against delivering a great user experience (i.e. the initial placeholder is ugly, the load times are slow, etc.).
In this section, we will try to solve some concerns around user experience with lazy loading of images.
Tip 1. Use the Right Placeholder
A placeholder is what appears in the container until the actual image is loaded. Normally, we see developers using a solid color placeholder for images or a single image as a placeholder for all images.
The examples we’ve looked at so far have used a similar approach: a box with a solid light gray background. However, we can do better to provide a more pleasing user experience. Below are some two examples of using better placeholders for our images.
Dominant Color Placeholder
Instead of using a fixed color for the image placeholder, we find the dominant color from the original image and use that as a placeholder. This technique has been used for quite some time by Google in its image search results as well as by Pinterest in its grid design.
This might look complex to achieve, but Manuel Wieser has an elegant solution to accomplishing this by scaling down the image to down to a 1×1 pixel and then scale it up to the size of the placeholder—a very rough approximation but a simple, no-fuss way to get a single dominant color. Using ImageKit, the dominant color placeholder can be obtained using a chained transform in ImageKit as shown below.
<!-- Original image at 400x300 -->
<img src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300" alt="original image">
<!-- Dominant color image with same dimensions -->
<img src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-1,h-1:w-400,h-300" alt="dominant color placeholder">
The placeholder image is just 661 bytes in size compared to the original image that is 12700 bytes—19x smaller. And it provides a nicer transition experience from placeholder to the actual image.
Here is a video demonstrating how this effect works for the user.
Low Quality Image Placeholder (LQIP)
We can extend the above idea of using a dominant color placeholder further. Instead of using a single color, we use a very low-quality, blurred version of the original image as the placeholder. Not only does it look good, but it also gives the user some idea about what the actual image looks like and the perception that the image load is in progress. This is great for improving the perceived loading experience. This technique has been utilized by the likes of Facebook and Medium.
LQIP image URL example using ImageKit:
<!-- Original image at 400x300 -->
<img src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300" alt="original image">
<!-- Low quality image placeholder with same dimensions -->
<img src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300,bl-30,q-50" alt="dominant color placeholder">
The LQIP is 1300 bytes in size, still almost 10x smaller than the original image and a significant improvement in terms of visual experience over any other placeholder technique.
Here is a video demonstrating how this effect works for the user.
It is clear that using either dominant color or LQIP placeholders provides a smoother transition from the placeholder to the actual image, gives the user an idea of what is to come in place of that placeholder, and improves loading perception.
Tip 2: Add Buffer Time for Images to Load
When we discussed different methods to trigger image loads, we checked for the point of time where the image enters the viewport, i.e. the image load is triggered when the top edge of the image placeholder coincides with the bottom edge of the viewport.
The problem with this is that users might scroll really fast through the page and the image will need some time to load and appear on the screen. Combined with throttling possibly further delaying the load, the user may wind up waiting a few milliseconds longer for the image to show up. Not great for user experience!
While we can get a pretty good user experience using the Intersection Observer API for performance and LQIP for smoother transitions, there is another simple trick that you can use to ensure that the images are always loaded completely when they enter the viewport : introduce a margin to the trigger point for images.
Instead of loading the image exactly when it enters the viewport, load it when it’s, let’s say, 500px before it enters the viewport. This provides additional time, between the load trigger and the actual entry in the viewport, for the images to load.
With the Intersection Observer API, you can use the root
parameter along with the rootMargin
parameter (works as standard CSS margin rule), to increase the effective bounding box that is considered to find the intersection. With the event listener method, instead of checking for the difference between the image edge and the viewport edge to be 0, we can use a positive number to add some threshold.
If you watch the following screencast closely, you’ll notice that the fifth image in the sequence is loaded when the third image is in view. Similarly, the sixth image is loaded when the fourth is in view, and so on. This way, we are giving sufficient time for the images to load completely and, in most cases, the user won’t see the placeholder at all.
If you didn’t notice earlier, in all our examples, the third image (image3.jpg
) is always loaded upfront, even though it is outside the viewport. This was also done following the same principle: load slightly in advance instead of loading exactly at the threshold for better user experience.
Tip 3: Avoid Content Reflow
This is another trivial point, which if solved, can help maintain a good user experience.
When there is no image, the browser doesn’t know the size it will take up. And if we do not specify it using CSS, then the enclosing container would have no dimensions, i.e. it will be read as 0x0 pixels.
When the image loads, the browser will drop it into the screen and reflow the content to fit it. This sudden change in the layout causes other elements to move around and it is called content reflow, or shifting. Michael Scharnagl goes into great depth explaining how this creates an unpleasant user experience.
This can be avoided by specifying a height and/or width for the enclosing container so that the browser can paint the image container with a known height and width. Later, when the image loads, since the container size is already specified and the image fits into that perfectly, the rest of the content around that container does not move.
Tip 4: Avoid Lazy Loading Every Image
This is a mistake that developers often make because it’s super tempting to think that deferring image loads is good all the time. But, like life itself, it is possible to have too much of a good thing. Lazy loading might reduce the initial page load, but it also might result in a bad user experience if some images are deferred when they should not be.
We can follow some general principles to identify which images should be lazy-loaded. For example, any image that is present in the viewport, or at the beginning of the webpage, should probably not be lazy-loaded. This applies to any header image, marketing banner, logos, or really anything that the user would see when initially landing on a page. Also, remember that mobile and desktop devices will have different screen sizes and hence a different number of images that will be visible on the screen initially. You’ll want to take the device that’s being used into account and decide which resources to load upfront and which to lazy load.
Another example is any image that is even slightly off the viewport in the initial load should not probably not be lazy-loaded. This is going by the principle discussed above—load slightly in advance. So, let’s say any image that is 500px or a single scroll from the bottom of the viewport can be loaded upfront as well.
One more example is if the page is short. It may be just a single scroll or a couple of scrolls, or perhaps there are less than five images outside the viewport. In these cases, you can probably leave lazy loading out altogether. It would not provide any significant benefit to the end-user in terms of performance and the additional JavaScript that you load on the page to enable lazy loading will offset any potential gain you get from it.
Chapter 5: Lazy Loading’s Dependency on JavaScript
The entire idea of lazy loading is dependent on JavaScript being enabled and available in the user’s browser. While most of your users will likely have JavaScript enabled, it is essential to plan for cases where it is not.
You could either show a message telling users why the images won’t load and encourage them to either use a modern browser or enable JavaScript.
Another route is to use the noscript
tag. However, this approach comes with some gotchas. This question thread on Stack Overflow does a great job addressing these concerns and is a recommended read for anyone looking to address this set of users.
Chapter 6: Popular JavaScript Libraries for Lazy Loading
Since environments and implementation details can vary across browsers and devices, you might want to consider a tried and tested library for lazy loading rather than spinning something up from scratch.
Here is a list of popular libraries and platform-specific plugins that will allow you to implement lazy loading with minimal effort:
- Yet Another Lazy Loader: This library uses the Intersection Observer API and falls back to event-based lazy loading for browsers that do not yet support it. This is great for just about any HTML element but unfortunately does not work on background images in CSS. The good news is that it supports IE back to version 11.
- lazysizes: This is a very popular library with extensive functionality. It includes support for responsive image
srcset
andsizes
attributes and provides superb performance even though it does not make use of the Intersection Observer API. - WordPress A3 Lazy Load: There are plenty of lazy loading WordPress plugins out there, but this one comes with a robust set of features, including a fallback when JavaScript is unavailable.
- jQuery Lazy: A simple library that uses a jQuery implementation.
- WeltPixel Lazy Loading Enhanced: A Magento 2 extension.
- Magento Lazy Image Loader: Another Magento extension, for 1.x.
- Shopify Lazy Image Plugin (paid): Enable lazy loading on a Shopify site.
Chapter 7: Testing Lazy Load
Once you have implemented lazy loading, you will likely want to check that it’s working as intended. The simplest way would be to open up the developer tools in your browser.
From there, go to Network > Images. When you refresh the page for the first time, you should only see loaded images in the list.
Then, as you start scrolling down the page, other image load requests would get triggered and loaded. You can also notice the timings for image load in the waterfall column in this view. It would help you identify image loading issues if any or issues in triggering the image load.
Another way would be to run the Google Chrome Lighthouse audit report on your page after you have implemented the changes and look for suggestions under the “Offscreen images” section.
Conclusion
We have covered a lot of ground about lazy loading images! Lazy loading—if implemented well—can have significant benefits on your site’s performance while reducing the overall page size and delivery costs, thanks to deferring unnecessary resources upfront.
So, what are you waiting for? Get started with lazy loading images now! And, if you need a little reminder of how this works, save a copy of the following infographic.
This is a brilliant article, but why oh why oh why oh why did you use jQuery for this? Here is a tutorial on web performance and we are going to load an entire JS library for a couple of lines of javascript. This would have been so much better / more useful if done in vanilla JS.
Agreed. It is in any case serving a very small part of the overall functionality. Let me just redo the examples without jQuery.
@rnanwani I feel guilty about the tone of my comment – I was in a rush. If you did this with vanilla JS it would be hands down the best article I’ve seen all year.
Removed jQuery from the first few examples :) Will remove it from the rest as well.
Awesome read. Lazy loading images is one of the biggest things for improving perceived performance.
I’m not so keen on the blurry image method though (despite the ginormous popularity, e.g. every Medium publication). I’ve run some simple user testing on a deliberately slowed down connection / processing power and many seemed to think the images were intentionally blured (i.e. censored), or even doubt if it was premium content they should be logged in / subscribed to see, or “something..”
I know most people have extremely fast connection speeds, so the blurry image is shown for <1s, but it can get frustrating and confusing when it takes any more than that. I would rather provide any sign that the image is actually loading than leave it as is, just in case of slow or bloated connections, Lie-Fi, etc.
Great insight. Do you suggest displaying a loader over a LQIP?
good point! in my experience it is the best to use some subtle loading spinners for lazy loading images
Lazy load with “blur up” and scroll denounce for performance
https://github.com/Paul-Browne/lazyestload.js
I’m torn on lazy loading.
On one hand, it prevents data from being sent needlessly if it’s never shown, which can reduce costs (especailly on mobile which is often metered by the byte). Imposing additional costs for no reason is pretty much insulting, nobody likes to spend money for no real reason.
On the other hand, people with awful connections (like me) will prefer to open the page then wait for it to load fully (maybe doing something else meanwhile). Except lazy loading breaks this, as now the ugly delay to load is imposed every time an image shows into view (and it can easily get into many seconds, not to mention what would happen if the connection got interrupted by that point…). Having to wait every one or two screens is not fun and can feel just as insulting.
Of course optimizing image size is crucial (and I’m noticing a trend lately of awful settings being used or outright encouraged – stop making JPEG files with 100% quality and stop using PNG for photographic images, jeez), and you can also force to load upfront images that are important enough to get likely to get viewed, but I wonder what other stuff could be done to help account for both cases?
I’ve never thought about that. Good point.
Thank you for this very interesting article. I agree with a previous comment about the questionable usefulness of jQuery but I don’t mind, as the synthesis work here is so valuable.
A question to push the subject one step further: have you ever had the opportunity to practice Lazy Loading coupled with the use of RWD images (through the
src-set
andsizes
attributes in the<image>
element or the elements<picture>
and<source>
)? I find relatively little research on the subject.Great idea! I will try to research on this and cover it in a follow up post.
Lazysizes.js does lazyload with ‘scr-set’ and ‘sizes’ attributes.
Lazysizes.js gets my vote, it does many of the things above as well as taking care of srcset
Regarding Chapter 5: what are your thoughts on this article about not relying on JavaScript?
https://www.robinosborne.co.uk/2016/05/16/lazy-loading-images-dont-rely-on-javascript/
Another option with debouncing, fade-in effect, and CSS background support:
https://github.com/craigbuckler/progressive-image.js
Great article, few small notes:
It’s not entirely correct to say that lazy-loading images lead to pages loading faster. They will be smaller but not necessarily faster, depending on many factors. Browsers generally already de-prioritize images if it finds other requests more important to boost rendering of the page. The page load event will be faster, but this is not how performance is measured these days.
I’m surprised you’re not mentioning the bleeding edge solutions. For example, in Chrome Canary, you can already simply add “lazyload=”true” on the img element, and that’s it!
If the browser has lesser number of resources to prioritize or competing for the same bandwidth, it would have some impact on the load time. Having said that, yes browsers are getting intelligent in terms of prioritisation. The in-built lazyload that you are talking about being one of these improvements. Great idea! I will try to cover this in a separate post maybe. Thanks for reading this one! https://twitter.com/rnanwani
…and another one designed to lazyload anything:
https://freqdec.github.io/peekaboo/
Lazy load image demo:
https://freqdec.github.io/peekaboo/demo/srcset-lazyload/index.html
Element Animation demo:
https://freqdec.github.io/peekaboo/demo/pantone/index.html
Uses IntersectionObserver when possible, falls back to debounced onscroll.Optimised for performance.
any idea how can i implement that method in wordpress??
without a plugin of course.
We use Low Quality Image Placeholder technique extensively on our themes at Fuel Themes.
Thanks, very useful and completed. Why didn’t you mention vanilla-lazyload in the list of libraries? It’s 2.5k stars on GitHub, very popular and used in production by many e-commerce brands.
What about SEO? Will a Search Machine index Lazyload images?
As long as you provide the alt attribute on your tags SEO will be unaffected.
Thank you for the great article and sharing your minds of challenging the image loading optimisation that has the real profit benefits)) But challenging the loading images optimisation that has the real profit benefits is not as easy.
Designers, please, stop to put important image content into the style sheets ! Google Image search does not index and rank images from CSS background code ! In focusing on the immediate needs of web users, it often fails to consider long-term consequences.
I also don’t feel that putting two different sources into the same img tag is a perfect solution. Yes, it’s good and tricky as a general idea, but as for me it would be better to strategize developing according to responsible semantic and SEO needs. Why not to use responsive picture and source tags, thanks to them we always could count on secure blurred img source ?
Putting good quality images into data-src has a huge impact on the SEO and it’s known problem for CMS, because Google Image indexes the src attribute, at least at the preference, so in the search results this code solution will give only blurred images. Unfortunately, there are no prouves on exact Google technique for indexing all rendered stuff on the page.
About the work behind the scenes, why not to apply any of existing prefetching methods with lazy logic with DOM manipulations of the preconnect / prefetch / prerender strategies? Prefetch element maybe is the best solution for any media page content and are recommended by W3C to identify a resource that might be required by the next navigation, meaning paginated content or step-driven flows (scrolling event). Linking with prefetch attribute can help to browser to identify a resource of prefetched content, its type, sizes and even media queries.
This is awesome. Thank you. As I look around and listen, lazy load is huge on performance. Really great when necessary say for an art gallery page.
First of all, this has already bumped me up on my LIghthouse scores. Thanks so much Rahul! Your article really helped me embrace the fear that I had about the IntersectionObserver API!
One note however, the code with the fallbacks still doesn’t work with IE, because IE is a dinosaur and doesn’t support the
.forEach
loop in JavaScript. I realized a week after implementing this that my images weren’t showing up at all in IE11. I’m including some code below to how I fixed this myself:“`////////////////////// LAZY LOADER
document.addEventListener(“DOMContentLoaded”, function() {
var lazyloadImages;
});“`
The JavaScript fallback on the IntersectionObserver version wasn’t working on mobile for me at first (images weren’t loading).
I had to adjust the code to this:
} else {
var lazyloadThrottleTimeout;
lazyloadImages = document.querySelectorAll(“img.lazy”);
The code needed to be img.lazy instead of just .lazy
Now it works great and the images are loading
Incredibly thorough article! Thanks Rahul.
How would the LQIP image example work in WordPress without the imagekit.io service? Anybody have an idea of how to implement this?
Thanks for the awesome sum-up!