How lazy loading works

The classic implementation uses an Intersection Observer:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;  // swap the placeholder for the real URL
      observer.unobserve(img);    // no further observation needed
    }
  });
}, { rootMargin: '200px' });  // start loading 200px before it appears

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

rootMargin: '200px' is the buffer: the image starts loading while it is still 200 pixels of scrolling away. That removes the visible delay during fast scrolling.

The native version is simply an HTML attribute:

<img src="product.jpg" loading="lazy" alt="Product description" width="300" height="300" />

Width and height are mandatory — without them the browser reserves no space and you get CLS (Cumulative Layout Shift).

What benefits from lazy loading in e-commerce

Catalogue pages (PLP). Hundreds of product cards with images — without lazy loading the browser fetches every one at page open. With it, only the visible ones. Data saved: 60-90% for a typical listing of 48 to 96 products.

Recommendation widgets. Blocks such as “Similar products” or “Bought together” lower down the product page or on the cart page are natural candidates. The widget requests recommendations from the API only once it enters the viewport.

Images in the blog. Articles with illustrations inside long text — everything below the first screen goes lazy.

How not to damage Core Web Vitals

Element Recommendation
Hero image, logo loading="eager" (the default)
The first three or four product cards loading="eager"
The rest of the cards in a listing loading="lazy"
Recommendation widgets Lazy loading via Intersection Observer
Analytics snippets async or defer

Important: set explicit width and height on every lazy image. Without them the browser reserves no space, which makes content jump during scrolling (CLS above 0.1 is a Core Web Vitals failure).