Lazy Loading: How It Speeds Up Websites

Lazy loading delays resource loading until they are needed in the viewport.

Lazy Loading

Lazy loading is a performance technique that defers loading of non-critical resources such as images, videos, and components until they are actually needed. Instead of downloading everything when the page first loads, content is fetched as the user scrolls toward it, reducing initial page weight and improving perceived performance significantly.

What Is Lazy Loading

By default, browsers attempt to download all resources referenced in a page's HTML as soon as the document is parsed. For pages with many images, embedded videos, or heavy third-party content, this creates a large initial download burden that delays the time before users can interact with the page. On slower connections or mobile devices, this can make a page feel sluggish even when the server itself is fast.

Lazy loading defers the download of off-screen resources until the user is about to see them. The browser loads only what is visible in the initial viewport immediately, and everything else waits until the user scrolls close enough to trigger the download. The result is a smaller initial payload, faster first paint, and a better experience for users who never scroll to the bottom of a long page, since they never download content they never see.

How Lazy Loading Works

  1. The page loads and the browser begins parsing the HTML document
  2. Resources marked for lazy loading are recognised but not downloaded immediately
  3. Content visible in the initial viewport, above the fold, loads right away with full priority
  4. Off-screen resources are deferred, their download has not yet started
  5. As the user scrolls down, the browser monitors the distance between deferred resources and the viewport edge
  6. When a deferred resource enters the viewport or comes within a defined threshold distance of it, the download begins
  7. The resource downloads and renders just in time for the user to see it, ideally appearing before they reach it

Native Lazy Loading in HTML

The simplest and most widely recommended way to implement lazy loading for images and iframes is the native HTML loading attribute. It requires no JavaScript, no third-party libraries, and no configuration. The browser handles all the logic of monitoring the viewport and triggering downloads at the appropriate time.

Lazy loading an image:
<img src="photo.jpg" loading="lazy" alt="A descriptive caption" width="800" height="600">
Lazy loading an iframe:
<iframe src="https://www.youtube.com/embed/abc123" loading="lazy" title="Video title"></iframe>

The loading="lazy" attribute is supported in all modern browsers including Chrome, Firefox, Safari, and Edge. Always include explicit width and height attributes on lazy-loaded images. Without them, the browser does not know the dimensions of the image before it loads and cannot reserve the correct amount of space, which causes layout shifts as images pop in and damages your Cumulative Layout Shift score.

The third valid value for the loading attribute is eager, which is also the default behaviour. Setting loading="eager" explicitly tells the browser to load the resource immediately regardless of its position, which is useful for marking above-fold images as high priority when other images on the same page use lazy loading.

Benefits of Lazy Loading

BenefitHow It Helps
Faster Initial Page LoadOnly above-the-fold content is downloaded on the first load, reducing the total bytes the browser must process before rendering anything visible
Reduced Bandwidth UsageUsers who do not scroll past the fold never download images below it, saving data for them and reducing egress costs for you
Better LCP ScoreLargest Contentful Paint improves when the browser can focus its bandwidth on the most important visible element rather than competing with dozens of off-screen image downloads
Lower Server LoadFewer simultaneous requests arrive during the initial page load, reducing the peak demand on your servers and CDN
Better Mobile ExperienceMobile users on slower connections benefit most from deferred loading because each saved request has a proportionally larger impact on their experience
Improved Core Web VitalsReduced initial load weight improves multiple Core Web Vitals metrics that Google uses as ranking signals

Lazy Loading Approaches

ApproachHow It WorksBest For
Native HTML attributeAdd loading="lazy" to img or iframe tags. The browser handles all viewport monitoring with no JavaScript.Images and iframes. The recommended default for most use cases.
IntersectionObserver APIJavaScript observes when elements enter or approach the viewport and triggers loading programmatically by swapping a placeholder src for the real one.Custom lazy loading for any element type, including backgrounds and custom components not covered by the native attribute.
Framework code splittingReact, Vue, Angular, and Next.js support dynamic imports that defer loading JavaScript bundles for components until they are rendered.Deferring large UI components, modals, charts, and other heavy sections that are not needed on the initial render.
Intersection Observer with placeholdersShows a low-quality blurred placeholder or skeleton while the full resource loads, providing visual feedback and preventing layout shift.Image-heavy pages where a smooth loading experience matters more than simplicity of implementation.

Lazy Loading with IntersectionObserver

For use cases not covered by the native loading attribute, the IntersectionObserver API provides a performant JavaScript alternative. It notifies your code whenever a watched element enters or exits the viewport without requiring scroll event listeners, which are expensive to process continuously.

Basic IntersectionObserver example for lazy images:
// Store the real src in a data attribute
// <img data-src="photo.jpg" class="lazy" alt="Photo">

const images = document.querySelectorAll('img.lazy');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '200px' // start loading 200px before entering viewport
});

images.forEach(img => observer.observe(img));

The rootMargin option allows you to trigger loading before the element is fully in view. A value of 200px starts the download when the image is 200 pixels below the visible area, giving it a head start so it is ready by the time the user scrolls to it.

When NOT to Lazy Load

Lazy loading applied indiscriminately can harm performance rather than improve it. Deferring the wrong resources delays the most important content and damages Core Web Vitals scores. These are the cases where you should explicitly use eager loading or simply omit the lazy attribute.

  • Never lazy load the LCP image: The Largest Contentful Paint element is the most important visual element on the page. Deferring its load directly delays this critical metric and harms your Google ranking. Identify your LCP image and ensure it loads with full priority, either by omitting the lazy attribute or by using loading="eager" and adding fetchpriority="high".
  • Never lazy load hero images or above-fold banners: Any image visible without scrolling should load immediately. Lazy loading above-fold content introduces a blank space where the image should be while the user waits for it to download, creating a poor first impression.
  • Never lazy load logos or navigation icons: These elements are visible immediately on every page and are usually small enough that deferring them provides no meaningful bandwidth saving while potentially causing visible layout shifts.
  • Be cautious with the first few images in a list or grid: In a product grid or image gallery, the first row of images is visible on load. Only apply lazy loading to images below the first visible row.

Frequently Asked Questions

  1. Does lazy loading affect SEO?
    When implemented correctly, lazy loading does not harm SEO. Google's crawlers support native lazy loading using the HTML loading attribute and modern IntersectionObserver-based implementations. They render JavaScript and can discover and index lazily loaded images. Older JavaScript techniques that replace the src attribute in ways crawlers cannot follow, or that hide images from crawlers entirely, can cause indexing problems. Using the native HTML attribute is the safest approach for both performance and SEO compatibility.
  2. Is lazy loading only for images?
    No. The technique applies to any resource that is not needed immediately. Common candidates include iframes and embedded videos, JavaScript bundles through code splitting in frameworks like React and Next.js, entire UI components such as modals and tabs that are not visible on the initial render, background images applied through CSS, and map embeds or other heavy third-party widgets. The native HTML attribute covers images and iframes directly. Other resource types require JavaScript or framework-level solutions.
  3. What is eager loading?
    Eager loading is the default browser behaviour where resources are downloaded as soon as they are discovered in the HTML, regardless of whether they are visible to the user. You can explicitly request eager loading by setting loading="eager" on an img or iframe tag. This is useful when you want to mark certain elements as high priority alongside other elements on the same page that use lazy loading, ensuring the browser understands which resources to prioritise during the initial load.
  4. What is fetchpriority and how does it relate to lazy loading?
    The fetchpriority attribute is a complementary hint that tells the browser how to prioritise a resource relative to others of the same type. Setting fetchpriority="high" on your LCP image tells the browser to download it before other images, even if it appears later in the HTML. Setting fetchpriority="low" on non-critical images tells the browser it can deprioritise them in favour of more important resources. Used together, loading="lazy" and fetchpriority give you precise control over both whether and how quickly resources are fetched.
  5. Can lazy loading cause layout shift?
    Yes, if images do not have explicit dimensions set. When the browser encounters a lazy image without defined width and height attributes, it does not know how much space to reserve for it before the download begins. When the image finally loads, it pushes other content down the page, causing a Cumulative Layout Shift. Always include width and height attributes on all images, including lazy-loaded ones. Setting them ensures the browser reserves the correct space from the start and the image loads into that space without moving anything else.

Conclusion

Lazy loading is one of the simplest and highest-return performance improvements available to web developers. Adding loading="lazy" to images and iframes below the fold takes seconds and can meaningfully reduce initial page weight, improve load times, and boost Core Web Vitals scores. The key is applying it selectively: defer everything below the fold, but never defer the LCP image or any above-fold content that users see immediately. For more advanced use cases, the IntersectionObserver API and framework-level code splitting extend the technique beyond images to any component or resource on your page. Continue with browser rendering, the critical rendering path, and CDN to build a complete performance optimisation strategy.