← Back to blogSEO & Performance

Lazy Loading Images: A Complete Guide for 2026

Lazy Loading Images Guide

Lazy loading images defers off-screen images from downloading until they are about to enter the viewport. The result is faster page loads, lower bandwidth, and better Core Web Vitals scores. Modern browsers support the native loading="lazy" attribute, frameworks like Next.js and Framer handle it automatically, and the IntersectionObserver API powers custom implementations. The pitfall: never lazy-load above-the-fold images, especially the LCP element.

Why Lazy Loading Helps Core Web Vitals

The default browser behavior is to download every image referenced in the HTML, regardless of whether the visitor will ever scroll to see it. On an article with 30 images, that means 30 downloads, parsing, decoding, and network bandwidth — much of it wasted on images the visitor never reaches.

Lazy loading inverts the pattern. Only the images visible on first paint download immediately. Everything else waits until the visitor scrolls close to it. The page loads faster, the bandwidth bill drops, and Largest Contentful Paint improves because the browser is not contending with image downloads it does not need yet.

The impact on metrics is real. A page with ten heavy below-the-fold images often sees LCP improve by 30% or more after lazy loading is implemented. The connection is straightforward: fewer competing downloads on the critical path means the LCP element gets bandwidth and CPU sooner. The full Core Web Vitals guide covers how lazy loading interacts with the broader performance picture.

The Native loading=”lazy” Attribute

The simplest way to lazy-load an image is the native HTML attribute:

<img src="example.jpg" loading="lazy" width="800" height="600" alt="Example">

That is the entire implementation. The browser handles the rest. Chrome, Edge, Firefox, and Safari all support loading="lazy" as of 2022. The attribute also works on iframes for embedded videos, maps, and similar elements.

The browser uses heuristics to decide when to start downloading: typically when the image is within a few hundred pixels of the viewport. The exact threshold varies by browser and connection speed, but it is tuned conservatively so the image is almost always loaded by the time it enters view.

What loading=”lazy” Does Not Do

Two things to know. First, it does not work on background images set in CSS. Background images load when their containing element renders. To lazy-load a background image, you have to use IntersectionObserver and toggle the CSS class manually.

Second, it does not lazy-load images above the fold. The browser is smart enough to recognize that an image visible on first paint should load immediately. Adding loading="lazy" to a hero image does not actually defer it — the browser ignores the hint when the image is in the initial viewport.

The IntersectionObserver API for Custom Cases

For cases the native attribute does not cover — background images, third-party widgets, complex lazy-load patterns — IntersectionObserver is the modern API.

The pattern: observe each lazy-loadable element, and when it intersects the viewport (or comes within a defined margin), swap a placeholder for the real source. Modern frameworks (React, Vue, Svelte) all have lazy-load components built on this primitive.

A bare-bones implementation looks like:

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' });

The rootMargin pre-loads images 200 pixels before they enter the viewport, smoothing the experience so visitors do not see images popping in as they scroll.

For most use cases, the native loading="lazy" attribute is enough. Reach for IntersectionObserver only when you need behavior the native attribute does not support.

Framework Support

Next.js Image Component

The Next.js <Image> component handles lazy loading automatically. Below-the-fold images get loading="lazy", the LCP image (when marked with priority) gets loading="eager" and a high fetchpriority hint. Width and height are required, which prevents Cumulative Layout Shift. The component also generates responsive srcsets and serves modern formats (WebP, AVIF) automatically.

Framer Image Component

Framer’s image component handles lazy loading by default. Below-the-fold images defer until they approach the viewport. The platform also generates responsive image sizes and serves modern formats automatically — most Framer sites pass Core Web Vitals without manual configuration. The Framer components guide covers how images interact with other components.

Astro Image Component

Astro’s <Image> and <Picture> components handle lazy loading with sensible defaults. Below-the-fold images get the loading="lazy" attribute. Above-the-fold images can be marked with loading="eager" to opt out.

WordPress

WordPress 5.5 and later add loading="lazy" automatically to images in post content. The behavior is configurable through filters. Plugins like Smush, ShortPixel, and EWWW Image Optimizer extend the behavior to handle background images, custom blocks, and edge cases.

Webflow

Webflow added native lazy loading attributes to its image elements. The default is to lazy-load all images; the editor allows toggling to eager loading per image, which is essential for the hero. The Webflow vs Framer SEO comparison covers performance differences in detail.

The Pitfall: Lazy-Loading Above the Fold

The single biggest mistake with lazy loading is applying it indiscriminately. If your hero image is the LCP element and you mark it loading="lazy", two things happen.

First, in browsers that respect the attribute literally, the image waits to download until layout calculations confirm its viewport position. That introduces delay. LCP gets worse, sometimes by hundreds of milliseconds.

Second, the browser does not give the image a high fetch priority. It assumes the image is non-critical because you marked it lazy. Modern browsers correct for this, but on older browsers or in edge cases, the LCP image ends up loading later than necessary.

The rule: eager-load anything visible in the viewport on first paint. Lazy-load only what is below the fold. Get the boundary wrong and you have hurt the metric you were trying to improve. The website speed optimization guide covers the broader principle of treating critical and non-critical assets differently.

Best Practices for Lazy Loading

Eager-Load the Hero

The hero image, primary product image, or any image visible above the fold should not be lazy-loaded. Use loading="eager" explicitly, and consider adding fetchpriority="high" to give the browser an extra hint. The image optimization guide covers prioritization in depth.

Set Width and Height

Lazy-loaded images cause CLS if dimensions are not specified. The element renders at zero height initially and inflates when the image arrives, pushing content down. Always include width and height attributes (or aspect-ratio in CSS).

Use rootMargin to Pre-Load

For custom IntersectionObserver implementations, set a generous rootMargin (200-400 pixels) so images start loading before they enter view. The visitor never sees an image pop in mid-scroll. The native loading="lazy" attribute does this automatically.

Combine with Modern Formats

Lazy loading reduces how many images load at once. Modern formats reduce how big each image is. Together, they compound. WebP cuts file sizes by 25-35% versus JPEG; AVIF can cut another 20-50%. Use the <picture> element with multiple sources so each browser picks its best format.

Test on Slow Connections

Lazy loading behavior matters most on slow networks. Test with Chrome DevTools’ “Slow 3G” throttling. If images do not appear in time as the visitor scrolls, the rootMargin needs to be larger or the images themselves need to be lighter.

Consider LQIP (Low-Quality Image Placeholders)

For sites where image perception matters (portfolios, ecommerce, editorial), low-quality image placeholders give visitors something to see before the full image loads. Tiny, blurry versions render instantly, then the full image fades in. Plaiceholder, BlurHash, and Next.js Image’s blurDataURL all implement this pattern.

Lazy Loading Beyond Images

The same principle applies to other heavy resources.

iFrames support loading="lazy" natively. Embedded YouTube videos, maps, calendars, and similar widgets benefit enormously — a YouTube embed downloads several megabytes of JavaScript even before playback. Lazy loading defers all of that until the visitor scrolls to it.

Third-party scripts (chat widgets, analytics, A/B testing tools) should be loaded after first paint, often after user interaction. Use defer, async, or dynamic imports to keep them off the critical path.

Fonts can be lazy-loaded for non-critical text using font-display: swap and the Font Loading API. The website typography guide covers the trade-offs.

How to Audit Lazy Loading on Your Site

Lighthouse

Lighthouse flags off-screen images that are not lazy-loaded under the “Defer offscreen images” audit. Run a Lighthouse audit in Chrome DevTools and address any flagged images.

PageSpeed Insights

The “Defer offscreen images” diagnostic surfaces opportunities. The “Properly size images” and “Serve images in next-gen formats” diagnostics catch related issues that compound with lazy loading.

Manual Inspection

Open your site in Chrome, scroll slowly while watching the Network tab. Each new image request is a new download. If the LCP image appears in the request waterfall later than necessary, you have a misconfigured lazy load. If below-the-fold images all appear immediately on page load, lazy loading is missing.

For a deeper performance audit, the Framer Websites team regularly evaluates sites for image strategy as part of speed reviews.

Frequently Asked Questions

Should I lazy-load every image?

No. Lazy-load only images below the fold. The hero image, product image, or anything visible on first paint should load eagerly. Lazy-loading above-the-fold images delays the LCP element and hurts perceived performance.

Does lazy loading help SEO?

Indirectly. Lazy loading improves Core Web Vitals, especially LCP and total page weight, which Google factors into rankings. Image SEO itself (alt text, descriptive filenames, structured data) is unaffected by lazy loading — search engine crawlers see the markup either way.

What is the difference between native and IntersectionObserver lazy loading?

Native lazy loading uses the loading="lazy" HTML attribute. The browser handles everything. IntersectionObserver is a JavaScript API for custom cases — background images, third-party widgets, advanced patterns. Use native unless you need behavior it does not support.

Can lazy loading cause Cumulative Layout Shift?

Yes, if dimensions are not specified. A lazy-loaded image without width and height starts at zero height and inflates when it loads, shifting content below it. Always set width and height attributes or use the aspect-ratio CSS property to reserve space before the image arrives.

Ready to build your Framer website?

Book a free strategy call to discuss your project.