Lazy Loading: A Complete Guide

Load only what the user needs to see to improve performance, data usage and experience

9 min

Lazy loading is an optimisation technique that defers the loading of non-visible resources until they are about to enter the user’s viewport. Instead of downloading all images, videos and components on page load, only what the user will see first is loaded. The rest loads on demand.

The impact is significant: pages with 50 images can reduce their initial load by 80% or more. This improves Largest Contentful Paint (LCP), reduces mobile data consumption and frees bandwidth and browser resources for priority content.

How does lazy loading work?

The principle is simple: resources outside the viewport are marked as candidates for deferred loading. When the user scrolls and the resource approaches the visible area, the download begins. This can be implemented with the native HTML attribute loading="lazy", with the JavaScript Intersection Observer API or with specialised libraries.

The browser manages the logic of when to start downloading in the native case: it typically begins loading when the resource is about 1,250 px from the viewport on fast connections and closer on slow connections, automatically adapting to the user’s conditions.

Image lazy loading

Images are the primary use case for lazy loading because they account for the bulk of most web page weight. The native loading="lazy" attribute on the <img> tag is the simplest and most efficient implementation.

  • Native: <img src="photo.webp" loading="lazy" width="800" height="600" alt="Description"> — no JavaScript required, universal support in modern browsers
  • Always set width and height to prevent layout shifts (CLS) when the image loads
  • Do not apply lazy loading to the hero or LCP image: it should load immediately with fetchpriority="high"
  • For CSS backgrounds, use Intersection Observer to add the class with the background image when the element approaches the viewport

Intersection Observer API

The Intersection Observer API is the modern way to implement lazy loading with JavaScript. It allows you to observe when an element enters or exits the viewport (or another container) efficiently, without the performance problems of the old scroll-event-based approach.

You create an IntersectionObserver with a callback that executes when observed elements cross a visibility threshold. The rootMargin allows you to define a margin around the viewport to start loading before the element is visible, preventing the user from seeing empty space.

  • Use rootMargin to pre-load before the element is visible: rootMargin: "200px 0px" loads with 200 px of anticipation
  • Threshold 0 (default) triggers when any pixel of the element is visible
  • Disconnect the observer (unobserve) once the resource has loaded to free memory
  • Global support >97%: no polyfill needed in modern browsers

Component lazy loading

Beyond images, lazy loading JavaScript components reduces the initial application bundle. Heavy components like WYSIWYG editors, interactive maps, charts or complex modals can be loaded on demand when the user interacts with them.

React offers React.lazy() with Suspense for dynamic component imports. Vue has defineAsyncComponent(). In Astro, the client:visible and client:idle directives apply native lazy loading to interactive components (islands architecture). Next.js supports dynamic imports with next/dynamic.

Video and iframe lazy loading

Iframes (especially from YouTube, Google Maps or social media embeds) are among the heaviest resources a page can contain. A YouTube embed loads over 1 MB of JavaScript and CSS, even if the user never plays the video.

  • Use loading="lazy" on iframes: <iframe src="..." loading="lazy"> — natively supported
  • For YouTube: use lite-youtube-embed or youtube-lite which shows a static thumbnail and only loads the player on click
  • For Google Maps: show a static image (Static Maps API) and load the interactive map on click or scroll
  • Facading: show a lightweight static version of the embed and load the real one only when the user interacts

Impact on performance and SEO

Lazy loading improves Core Web Vitals by reducing the initial page weight, but it must be implemented correctly to avoid negatively affecting SEO. Googlebot processes JavaScript and can render Intersection Observer-based lazy loading, but deferred content may take longer to be indexed.

Images with native loading="lazy" are indexed correctly by Google. For important textual content, avoid lazy loading: main content should be available in the initial HTML to ensure immediate indexation. Use lazy loading only for resources below the fold.

  • LCP: improves by reducing bandwidth competition with the main image
  • CLS: can worsen if you do not set width/height on lazy loaded images
  • INP: improves by reducing initial JavaScript competing for the main thread
  • Indexation: Googlebot renders lazy loading but may take longer to discover deferred content

Common lazy loading mistakes

Poorly implemented lazy loading can worsen the experience instead of improving it. The most frequent mistakes include applying lazy loading to the LCP image (delaying its load), not defining dimensions (causing layout shifts) and using heavy JavaScript implementations when the native attribute would suffice.

Another common mistake is overly aggressive lazy loading: if the user scrolls fast, images do not load in time and they see empty spaces. Use rootMargin in Intersection Observer or rely on the adaptive behaviour of the native attribute to mitigate this issue.

Key Takeaways

  • The loading="lazy" attribute is the simplest and most efficient form of image lazy loading
  • Never apply lazy loading to the hero/LCP image: load it with fetchpriority="high"
  • Intersection Observer replaces scroll events with superior performance and control
  • YouTube and Google Maps embeds are ideal candidates for lazy loading with the facade pattern
  • Always set width and height on lazy loaded images to prevent layout shifts (CLS)

Is your site loading more than it needs to?

We audit your site to identify lazy loading opportunities that improve Core Web Vitals and reduce initial load time.