Lazy load images without JavaScript
Lazy loading images used to mean writing an IntersectionObserver, swapping src attributes, and handling edge cases per browser. One attribute does it all now.
2<img src="hero.jpg" alt="Hero" />
3
4<!-- Below the fold: lazy load -->
5<img src="photo.jpg" alt="Photo" loading="lazy" />
6<iframe src="map.html" loading="lazy"></iframe>
2<img data-src="photo.jpg" class="lazy" />
3const observer = new IntersectionObserver(([entry]) => {
4 if (entry.isIntersecting) {
5 entry.target.src = entry.target.dataset.src;
6 observer.unobserve(entry.target);
7 }
8});
9document.querySelectorAll('.lazy').forEach(img => observer.observe(img));
Browser Support for Lazy loading
This feature is well established and works across many devices and browser versions. It has been available across browsers since 2023.
One attribute
Add loading="lazy" to any img or iframe. The browser decides when to fetch it based on scroll position and network conditions.
Faster initial load
Off-screen images are not fetched on page load. The browser only requests them as the user scrolls near them, saving bandwidth and speeding up LCP.
Works on iframes too
loading="lazy" works on iframe elements as well. Useful for maps, embedded videos, and third-party widgets that would otherwise block rendering.
How it works
The loading attribute tells the browser how to prioritize fetching the resource. loading="lazy" defers the fetch until the image or iframe is near the viewport. loading="eager" is the default β fetch immediately regardless of position.
Never add loading="lazy" to above-the-fold images. Those need to load immediately for good LCP scores. Apply it only to images that start off-screen. A good rule: the first image on a page should not be lazy.
Always include width and height attributes on lazy images. Without them the browser does not know the image dimensions before it loads, causing layout shift when the image finally appears.