The quick download:
Image lazy loading speeds up your site only when you apply it to the right images and prove the gain with performance data.
-
Deferring offscreen images cuts the initial payload and frees bandwidth for the hero image, scripts, and styles that drives the first render.
-
Lazy-load the wrong images and you delay LCP; skip reserved dimensions and you increase CLS.
-
Early browser image requests are normal, so DOM checks alone cannot confirm that lazy loading is working.
-
Run a controlled before-and-after test in WebPageTest, then validate in production with synthetic and real user monitoring so the gains survive every release.
On most pages, images are the heaviest resource the browser downloads. When large offscreen images compete with critical scripts, styles, and the hero image during the first moments of rendering, they slow down what the user actually sees. Image lazy loading is one of the most effective ways to ease that contention. It defers offscreen images until the user is about to reach them, which reduces the initial payload and frees bandwidth for the content that matters first.
Lazy loading is not automatically beneficial. Applied to the wrong images, it can delay the Largest Contentful Paint (LCP) and hurt the experience it was meant to improve. The technique earns its value when you apply it selectively and then measure the result.
This article explains how image lazy loading works, how to implement it correctly, and how to validate that those optimizations continue improving user experience over time.
What is image lazy loading?
Image lazy loading is a web development technique that defers loading an image until it is needed, usually when it is close to the user’s viewport. There are three common ways to achieve it, and they behave differently:
- Native browser lazy loading through the loading=”lazy” attribute, handled by the browser with no JavaScript.
- The Intersection Observer API, where your own JavaScript detects when an image nears the viewport and loads it.
- Third-party libraries built on top of Intersection Observer for extra control.
Native lazy loading does not produce a placeholder or low-resolution thumbnail on its own. If you want a blur-up preview or a reserved space before the full image loads, you build that separately with a placeholder image and explicit width and height (or an aspect-ratio) so the layout stays stable. Reserving space is what prevents content from shifting as images load, which protects your Cumulative Layout Shift (CLS) score.
Native lazy loading
Modern browsers support the loading attribute, which lets images load lazily with no extra JavaScript. The attribute accepts two values, lazy and eager:
- lazy defers the image until it is near the visible area of the page, which can reduce initial network contention and transferred bytes.
- eager loads the image immediately, even before it is visible.
If you omit the attribute, the browser uses its default eager behavior and loads the image right away. MDN documents only these two values, so there is no auto value in the specification, and setting loading=”auto” falls back to the default.
<!– Defer an offscreen image –>
<img src=”image.jpg” loading=”lazy” alt=”Descriptive text”>
<!– Load an above-the-fold image right away –>
<img src=”hero.jpg” loading=”eager” alt=”Descriptive text”>
The most important rule is to load above-the-fold images eagerly and keep the LCP image out of the lazy-loaded set. Google’s guidance is explicit on this point: lazy loading the LCP image delays that paint and lowers your score, so keep it out of the lazy-loaded set. For a critical hero image, you can go further and raise its priority with the fetchpriority=”high” attribute so the browser fetches it ahead of less important resources.
<img src=”hero.jpg” fetchpriority=”high” alt=”Descriptive text”>
Lazy loading and Core Web Vitals
Lazy loading is a Core Web Vitals tradeoff, not a free win. Used well on offscreen images, it lowers the bytes the browser downloads before first render and reduces contention for the network, which helps LCP for the content in view. Used carelessly, it does the opposite:
- Lazy loading an in-viewport image, especially the LCP image, forces the browser to discover and fetch it later than it should, which delays LCP.
- Loading images without reserved dimensions causes layout shifts as they load, which raises CLS.
The takeaway is to be selective. Defer images below the fold, load critical images eagerly, reserve space for every image, and confirm the effect on LCP and CLS with real measurement.
Intersection Observer API
Native lazy loading covers most needs. Reach for the Intersection Observer API when you need behavior the native attribute does not offer, such as custom load thresholds, lazy loading CSS background images, triggering animations as elements enter view, or coordinating loads with a component’s lifecycle. It lets your JavaScript observe when a target element intersects the viewport and load the image at that moment.
<img data-src=”path/to/image.jpg” alt=”Descriptive text”>
<script>
const images = document.querySelectorAll(‘img[data-src]’);
const options = { rootMargin: ‘0px’, threshold: 0.5 };
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
}, options);
images.forEach(img => {
observer.observe(img);
});
</script>
Choosing a third-party library
Most sites do not need a library. Use native lazy loading as your default and reserve a library or the Intersection Observer API for cases with custom thresholds, background-image handling, complex component lifecycles, or specific compatibility requirements. If you do need one, choose a maintained, actively supported project and confirm recent releases before adding it. vanilla-lazyload is one actively maintained option. Some widely referenced libraries, such as lazysizes and Lozad.js, have not shipped a release in several years, so check the project’s release history and open issues before you rely on it. Whatever you pick, test for conflicts with other scripts and confirm it improves performance rather than assuming it does.
How to test whether image lazy loading is working
A common shortcut is to open developer tools, load a page without scrolling, and treat any offscreen image request as proof that lazy loading is off. That test is unreliable. Browsers use their own preload thresholds and can request a lazy-loaded image before it becomes visible, so an early request does not mean the attribute failed.
You can still use a quick DOM heuristic to detect common markup patterns. The script below lists images that have neither loading=”lazy” nor a [data-src] hook and are outside the viewport. Treat it as a check on your markup, not as proof of actual loading behavior.
function findEagerOffscreenImages() {
let images = document.querySelectorAll(
‘img:not([data-src]):not([loading=”lazy”])’
);
return Array.from(images).filter((tag) => !inViewport(tag));
}
function inViewport(tag) {
let box = tag.getBoundingClientRect();
return (
box.bottom >= 0 &&
box.right >= 0 &&
box.top <= (window.innerHeight || document.documentElement.clientHeight) &&
box.left <= (window.innerWidth || document.documentElement.clientWidth)
);
}
console.log(“Images without a lazy-loading hook:”, findEagerOffscreenImages());
To know whether lazy loading actually helped, run a controlled before-and-after experiment under network throttling and compare:
- A request waterfall and image-request timing, so you can see which images load and when.
- A filmstrip view of the render, so you can compare how fast meaningful content shows across the two versions.
- Transferred image bytes before any scrolling.
- LCP and CLS before and after the change.
Repeat the test across mobile and desktop profiles, since network conditions and screen size change the result. A no-code experiment in WebPageTest is a good way to establish this controlled before-and-after comparison.

Controlled testing provides a useful performance baseline, but it cannot capture the full range of conditions users experience. Device capabilities, browsers, network quality, geographic location, and third-party dependencies can all affect how images load in production. Teams should continuously validate optimizations such as lazy loading across both controlled tests and real-user environments to confirm that improvements persist through deployments, dependency changes, and infrastructure updates.
Dos and don’ts of lazy loading
Dos
- Lazy load images that are not immediately visible, such as those below the fold or off-screen.
- Load above-the-fold images eagerly and give the LCP image high fetch priority.
- Reserve space with explicit width and height or an aspect-ratio so lazy loading does not raise CLS.
- Combine lazy loading with responsive images, correct sizing, and modern formats. Lazy loading does not make up for serving oversized assets.
- Monitor LCP and CLS after the change, and test across devices and browsers.
Don’ts
- Don’t lazy load the LCP image or other critical images the page needs to function, such as your logo or navigation elements.
- Don’t apply lazy loading globally without checking which images fall in the initial viewport.
- Don’t lazy load very small images that load with little delay, since the script overhead may outweigh the benefit.
- Don’t confuse lazy loading with accessibility. Every meaningful image needs descriptive alt text regardless of how it loads, and decorative images should use empty alt text.
- Don’t treat lazy loading as a substitute for optimizing images. Large or uncompressed files still hurt performance.
From optimization to continuous performance
Lazy loading changes how the page loads; measurement confirms that the change improves user experience. Treat the process in three stages.
Controlled testing. Use a lab tool such as WebPageTest to establish a repeatable before-and-after comparison under fixed conditions. Compare LCP, CLS, transferred image bytes, request timing, and the visual loading sequence to determine whether lazy loading produced the intended improvement.
Production validation. Lab tests cannot reproduce every combination of device, browser, network, location, and third-party dependency. Synthetic monitoring and real user monitoring complement controlled testing by showing whether the optimization improves actual user experience across these conditions, and whether the benefit persists after deployment.
Regression prevention. Lazy loading is not a set-it-and-forget-it optimization. CMS updates, design changes, third-party scripts, and routine releases can accidentally defer a critical image or remove the reserved space that protects CLS. Continuous monitoring helps teams detect these regressions before they affect a significant number of users.
Validate performance across the entire delivery path
Optimizations such as lazy loading only matter when they improve the experience users actually receive. Confirming that outcome requires visibility beyond the page itself into application health, third-party dependencies, infrastructure, and the Internet paths connecting users to the service.
LogicMonitor Digital Experience (IPM) brings together synthetic testing, real user monitoring, Internet-path visibility, and infrastructure observability. By connecting frontend performance with the systems and services supporting it, teams can determine whether a regression originates in the application environment, an external dependency, or the public Internet.
For a media or ecommerce site, teams can first use WebPageTest to confirm that lazy loading improves metrics such as LCP, CLS, and transferred image bytes. They can then monitor the live site across locations, devices, and networks to verify that users receive the same improvement, and detect when a future release causes those metrics to regress.
Prove your lazy loading gains reach real users across every device and network.
Use LogicMonitor to validate image-loading improvements with controlled testing, synthetic monitoring, and real user data, then catch regressions before they affect users.
FAQs
Does lazy loading always improve website performance?
No. It helps when applied to offscreen images, cutting the initial payload and easing network contention for the content in view. Applied to the LCP or above-the-fold images, it can delay rendering and hurt the experience it was meant to improve. The gain depends on applying it selectively and measuring the result.
How do you prevent lazy loading from introducing Cumulative Layout Shift (CLS)?
Reserve space for every image with explicit width and height or an aspect-ratio. That keeps the layout stable as images load and protects your Cumulative Layout Shift (CLS) score. Without reserved dimensions, images push content around as they arrive and raise CLS.
Do I need a third-party library to lazy load images?
Most sites do not. Native browser lazy loading through the loading=”lazy” attribute covers the majority of cases with no JavaScript. Reach for the Intersection Observer API or a maintained library only when you need custom thresholds, background-image handling, or coordination with a component’s lifecycle.




