Native image lazy loading: one attribute, used well
The loading attribute delays offscreen images until the user scrolls near them. It takes one word, but on the wrong image it makes a page slower. Where it belongs.
A long page can hold dozens of images, and most visitors never scroll to the bottom. Without lazy loading, the browser downloads all of them anyway. They compete with the content the visitor is looking at, and on mobile they spend data the visitor pays for.
Browsers can defer those images themselves. No library is needed.
The attribute
<img
src="/img/team.jpg"
alt="The team at the Dhaka office"
width="1200"
height="800"
loading="lazy"
/>
With loading="lazy", the browser waits until the image is close to the viewport before requesting it. Every current browser supports it. A very old browser ignores the attribute and loads the image normally, so nothing breaks and no JavaScript fallback is required.
Always set width and height
A deferred image has no size until it arrives, unless you state one. Without width and height, the text below jumps when the image appears. That is a layout shift, and it counts against your Core Web Vitals.
With both attributes, the browser reserves the right space from the start. The image can still be fluid in CSS:
img {
max-width: 100%;
height: auto;
}
Do not lazy load what is visible first
This is the common mistake. Adding the attribute to every image, including the hero image, makes the page slower. The browser holds back the most important image until layout is done, and the Largest Contentful Paint suffers.
Images at the top of the page should load immediately. Mark the main one as important:
<img src="/img/hero.jpg" alt="…" width="1600" height="900" fetchpriority="high" />
A simple rule: the hero image and anything visible without scrolling loads eagerly. Everything below that is lazy.
It works for iframes too
<iframe
src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
title="Product demo"
loading="lazy"
width="560"
height="315"
></iframe>
Embedded videos and maps are heavy. Deferring them often saves more than all the images together.
What it does not cover
CSS background images are not affected by the attribute. For those, and for anything that needs code to run when it becomes visible, use the Intersection Observer API.
Check that it works
Open the Network panel in DevTools, filter by images and reload. Only the images near the top should load. Scroll down and watch the rest arrive as you go.
Summary
| Image | Attribute |
|---|---|
| Hero or main image | fetchpriority="high", no lazy loading |
| Other images visible on load | Default loading |
| Everything below the fold | loading="lazy" |
| All images | width and height |
Lazy loading decides when an image loads. How heavy it is matters as much. See WebP and AVIF and the full guide to image optimization for the web.