Lazy Loading Images and Media: How Far Ahead Browsers Fetch
In this article
Lazy loading takes one HTML attribute and no JavaScript. On a long page, it skips most of the images, embeds and video a visitor never scrolls to, so the first screen loads sooner and less data is sent.
Most pages don’t use it. Chrome sees loading="lazy" on images for just 26% of page loads, and on iframes in 1.5%.
And 17% of pages lazy-load their LCP image, which makes them slower.
What loading="lazy" and eager do#
loading takes lazy or eager, when unset, eager is the default. The browser’s preload scanner finds an eager image in the HTML and fetches it straight away. A lazy image waits until layout has run and the element enters the viewport (or the user scrolls near to it).
What you can lazy load#
loading="lazy" works on <img>, <iframe>, <video> and <audio> elements:
<img src="my-image.avif" loading="lazy" />
<iframe src="my-slow-iframe.html" loading="lazy" />
<video src="showreel.mp4" loading="lazy" />
<audio src="tunes.ogg" loading="lazy" />Images and iframes
Video and audio
What should be lazy loaded?#
Nearly all below the fold media should be lazy-loaded to save on initial page render speed and data transfer.
iFrames are usually a big win with lazy loading, as they have their own internal network cascade and can more grossly affect performance.
Video and audio are newly available. A lazy video holds back its file, its poster and autoplay until it comes close.
How many pages use lazy loading?#
HTTP Archive found loading="lazy" on 44% of pages. Chrome’s feature use counters for September 2026 show lazy loading is barely used for images, and almost never used for anything else:
| Element | % of Chrome page loads using lazy load |
|---|---|
<img loading="lazy"> | 26% |
<iframe loading="lazy"> | 1.5% |
<video loading="lazy"> | 0.06% |
<audio loading="lazy"> | 0.000039% |
Support for iFrames arrived after images, and video and audio lazy loading support is new, explaining low usage.
How far ahead each browser fetches lazy images#
Every browser fetches lazy images before they reach the screen, and each one picks a different distance:
- Chrome changes the distance with connection speed. The slower the connection, the further ahead it fetches, up to 8,000px on Slow 2G.
- Firefox uses one fixed margin, 600px on every side, whatever the connection.
- Safari gives images a full viewport in every direction. A lazy iframe in Safari doesn’t load until it’s on screen.
Chrome gives video and audio the same distance as images. Firefox and Safari don’t lazy-load them yet, so they load straight away.
| Browser | Images | Iframes | Video and audio |
|---|---|---|---|
| Chrome, 4G | 1,250px | 2,500px | 1,250px |
| Chrome, 3G | 2,500px | 3,500px | 2,500px |
| Chrome, 2G | 6,000px | 6,000px | 6,000px |
| Firefox | 600px | 600px | Not supported |
| Safari | One viewport | 0px | Not supported |
Three ways lazy loading goes wrong#
Lazy-loading the LCP image#
While lazy-loading is a great feature, but it shouldn’t be thrown around willy-nilly: 17% of Pages lazy load the hero LCP image. Incorrectly adding lazy to your hero image will delay it. Best avoided!
On a mobile connection the stylesheet takes longer, and so does the image.
Leaving out width and height#
Lazy media arrives after layout, so it pushes content around unless the browser already knows its dimensions.
Give every lazy image, video and iframe a width and height, or a CSS aspect-ratio. It’s the cheapest Cumulative Layout Shift fix there is.
Lazy-loading everything#
A blanket loading="lazy" in a template can adversely affect page performance, similar to how preloading everything is a bad idea. It’s best to lazy-load below-the-fold elements only.
Anything on the first screen should stay eager.
Where to use lazy, eager and fetchpriority#
Carefully peppering your page with lazy and fetch priority optimisation hints means that browsers will fetch and display content more quickly. Here’s what we recommend:
With <picture>, put loading and fetchpriority on the <img>. The browser ignores them on <source>.
Frameworks like React incorrectly add a preload attribute to every image rendered by SSR. You can opt out of this behavior by adding lazy-loading, which is also counter-intuitive. Always audit the actual HTML rendered by your server.
Lazy rendered content#
loading="lazy" defers downloads, but you can also lazy render content in your pages for an additional speed boost.
CSS content-visibility#
Content visibility (content-visibility: auto) skips layout and paint for content that’s off-screen.
This saves time for needless rendering and painting. In web.dev’s travel blog example, rendering time dropped from 232ms to 30ms.
Pair it with contain-intrinsic-size, so a skipped section keeps a placeholder height and the scrollbar doesn’t jump:
.article-section {
content-visibility: auto;
contain-intrinsic-size: auto 500px;
}Skipped content stays in the DOM and the accessibility tree, so find-in-page and keyboard navigation still work.
JS IntersectionObserver: lazy anything!#
You can extend other areas of your page to respond when content is near-to or in the user’s viewport using an IntersectionObserver, which is well supported:
- Widgets that do more than fetch a file: a map, a chat widget
- CSS background images, which
loadingdoesn’t apply to - Video and audio in Firefox and Safari, which don’t support
loadingon them yet. web.dev shows how to start video with an observer - JavaScript you only need when a section appears, loaded with
import()
Using rootMargin you can customise how near-to the viewport content has to be before it’s initiated.
// Load map 600px before user scroll
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
import("./map.js").then(({ mountMap }) => mountMap(entry.target));
observer.unobserve(entry.target);
}
},
{ rootMargin: "600px" },
);
observer.observe(document.querySelector("#map"));True story: Most LLM Agents or Bots don’t scroll the page, so you can effectively hide content using IntersectionObserver to protect against abuse, and offer a superior user experience. Neat!
How to check lazy loading is working#
In DevTools:
- Open the Network panel and filter to Img and Media. Iframes show up under Doc.
- Tick Disable cache, reload, and don’t scroll. That list is everything fetched during load.
- Now scroll. Whatever arrives is what was deferred.
Check the Elements panel too, not just your templates. The attribute often gets added somewhere you didn’t write it.
In conclusion#
You can positively influence user-experiences by paying careful attention to how content is fetched and loaded using <link rel="preload">, loading="lazy" and fetchpriority attributes, but if you get it wrong, performance can suffer. Getting it right is almost a free win.
