// blog/developer/
Back to Blog
Developer · July 20, 2026 · 9 min read · Updated May 22, 2026

Image Lazy Loading for Faster Web Pages

Image Lazy Loading for Faster Web Pages

Images are the heaviest assets on most web pages. A typical blog post might have 5 to 10 images, each weighing 200KB to 2MB. If the browser downloads all of them on load, visitors wait for megabytes of data they may never scroll down to see.

Lazy loading defers image downloads until the image is about to enter the viewport. The browser only fetches images the user is going to see. For a page with 10 images where only 3 are visible without scrolling, lazy loading reduces the initial page weight by around 70%.

The result is faster initial load times, lower bandwidth usage, and better Core Web Vitals scores. And the basic version is a single HTML attribute.

Before you lazy load, make sure your images are sized and compressed. An uncompressed 4MB image is still 4MB even with lazy loading. Run them through the Image Compressor to cut file size without visible quality loss.

* * *

Native Lazy Loading with the loading Attribute

The simplest implementation is the native loading attribute:

`html Description `

That is it. The browser handles the rest. It monitors the scroll position and fetches the image when it gets close to the viewport (typically 1250 to 2500 pixels before the image scrolls into view, depending on connection speed).

Important details:

Always include width and height: without dimensions, the browser cannot reserve space for the image before it loads. This causes layout shifts (elements jumping around as images load), which hurts your Cumulative Layout Shift (CLS) score.

Do not lazy load above-the-fold images: images visible without scrolling should load immediately. Lazy loading them delays their appearance and hurts Largest Contentful Paint (LCP). Use loading="eager" (the default) for hero images, logos, and first-visible content.

Browser support: the loading attribute is supported in all major browsers since 2020. No polyfill needed in 2026.

Iframes too: loading="lazy" works on iframes as well. Embedded videos, maps, and third-party widgets can all be lazy loaded.

The Image Resizer prepares images at the exact dimensions you need, so the browser does not download a 4000px image just to display it at 800px.

Website loading performance metrics on screen
Website loading performance metrics on screen
* * *

Intersection Observer API for Advanced Control

When native lazy loading does not give you enough control, the Intersection Observer API lets you define exactly when and how images load:

`javascript const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); } }); }, { rootMargin: '200px 0px' });

document.querySelectorAll('img[data-src]').forEach(img => { observer.observe(img); }); `

The HTML uses data-src instead of src, with a tiny placeholder or blank pixel as the initial source:

`html Description `

Advantages of Intersection Observer over native lazy loading:

Custom threshold: control exactly how far from the viewport the image starts loading. The rootMargin option lets you set a buffer zone.

Loading animations: trigger fade-in animations when images load, creating a polished user experience.

Priority ordering: load images in a specific order rather than whatever the scroll position dictates.

Conditional loading: load different image sizes based on viewport width or connection speed.

The trade-off is more code to maintain. For most projects, native loading="lazy" is sufficient.

Key takeaway

When native lazy loading does not give you enough control, the Intersection Observer API lets you define exactly when and how images load: ```javascript const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); } }); }, { rootMargin: '200px 0px' }); document.querySelectorAll('img[data-src]').forEach(img => { observer.observe(img); }); ``` The HTML uses `data-src` instead of `src`, with a tiny placeholder or blank pixel as the initial source: ```html <img data-src="photo.jpg" src="placeholder.svg" alt="Description"> ``` Advantages of Intersection Observer over native lazy loading: **Custom threshold**: control exactly how far from the viewport the image starts loading.

* * *

Responsive Images and Lazy Loading Together

Modern websites serve different image sizes for different screen widths using srcset and sizes. Combining this with lazy loading gives you the best of both worlds:

`html Description `

The browser selects the appropriate image size from srcset based on the viewport width AND only downloads it when the image approaches the viewport thanks to loading="lazy". A mobile user never downloads the 1200px version, and neither the mobile nor the desktop user downloads anything until they scroll near the image.

For the element with different image formats:

`html Description `

The loading attribute on the inside applies to whichever source the browser selects. AVIF and WebP formats are 25 to 50% smaller than JPEG, and combined with lazy loading, the bandwidth savings multiply.

* * *

Impact on Core Web Vitals

Lazy loading directly affects two of the three Core Web Vitals:

Largest Contentful Paint (LCP): lazy loading below-the-fold images reduces the work the browser does during initial load, which can improve LCP. But lazy loading the LCP element itself (usually a hero image) hurts LCP because it introduces a delay. Identify your LCP element using Chrome DevTools or PageSpeed Insights and make sure it loads eagerly.

Cumulative Layout Shift (CLS): lazy loaded images without explicit width and height attributes cause layout shifts when they load. The image placeholder takes zero space, and when the image appears, everything below it jumps down. Always set width and height or use CSS aspect-ratio to reserve space.

Interaction to Next Paint (INP): lazy loading reduces the amount of DOM processing during initial load, which can indirectly improve INP by reducing main thread work.

Measure the impact after implementing lazy loading. Use Lighthouse, PageSpeed Insights, or Web Vitals extension to compare before and after scores.

Minify your HTML after implementing lazy loading to keep page weight down. The HTML Minifier removes whitespace and comments from your markup without changing behaviour.

Developer optimizing website speed on laptop
Developer optimizing website speed on laptop
* * *

Common Lazy Loading Mistakes

Lazy loading everything: do not apply loading="lazy" to every image on the page. Above-the-fold images (typically the first 1 to 3 images) should load immediately. Lazy loading them delays the initial visual experience and hurts performance metrics.

Missing dimensions: the most common CLS issue with lazy loading. Without width and height, the browser allocates zero space for the image. When it loads, the page reflows. Always specify dimensions.

Placeholder images that are too large: using a 50KB blurred placeholder defeats the purpose of lazy loading. Use tiny SVG placeholders (under 1KB), solid color backgrounds, or CSS aspect-ratio boxes.

Not compressing lazy loaded images: lazy loading reduces when images load, not how large they are. A 3MB image is still 3MB when it finally loads. Compress all images regardless of loading strategy.

JavaScript-only lazy loading without fallback: if you use Intersection Observer and JavaScript fails to load (network error, script blocked), no images will ever load. Always provide a

Infinite scroll without limits: lazy loading content in an infinite scroll without any pagination fallback creates accessibility issues, makes specific content unfindable, and uses unbounded memory. Add pagination as an alternative navigation method.

* * *

FAQ

Does lazy loading affect SEO?

No, as long as you use native loading="lazy" or properly implemented Intersection Observer with

Should I lazy load background images set via CSS?

CSS background images are not affected by the HTML loading attribute. To lazy load CSS backgrounds, use Intersection Observer to add a CSS class that sets the background-image only when the element enters the viewport. This works well for hero sections that are initially below the fold.

How far from the viewport should lazy loading trigger?

The browser handles this automatically with native lazy loading, typically starting the download 1250 to 2500 pixels before the image enters the viewport. With Intersection Observer, a rootMargin of 200 to 500 pixels works well for most connections. Too small and users see loading placeholders; too large and you lose the bandwidth savings.

Does lazy loading work on mobile?

Yes. All modern mobile browsers support native lazy loading. Mobile is where lazy loading provides the biggest benefit because mobile connections are often slower and data is more expensive. The bandwidth savings on mobile can sharpen the user experience by several seconds on real-world connections.

Key takeaway

### Does lazy loading affect SEO.