Lazy Loading Images in Ecommerce: When It Helps, When It Hurts, and How to Get It Right
Lazy-loaded images can save 40% on page weight or destroy your LCP score depending on implementation. Here's the rule for product pages and a step-by-step setup that actually works.
Lazy loading sounds like a free performance win — load only the images the visitor actually scrolls to. In practice, it's one of the most commonly mis-implemented techniques in ecommerce, and a wrong setup can hurt your Core Web Vitals more than no lazy loading at all.
This guide explains when lazy loading helps, when it hurts, and the exact setup that works on product pages, category pages, and homepages.
What Lazy Loading Actually Does
By default, browsers download every image referenced in HTML as soon as they parse the tag — even images far below the fold. On a product detail page with 15 alternate angles and a "frequently bought together" carousel, that's 15-30 image requests competing for bandwidth before the first viewport renders.
Lazy loading defers off-screen images until the visitor scrolls toward them. The browser knows because of loading="lazy" on the <img> tag (native lazy loading) or because JavaScript intercepts and loads on scroll (manual lazy loading).
Where Lazy Loading Helps
- Long category pages: 50+ products on a single page. Only 6-8 visible above fold. Lazy loading saves 80% of image bytes for users who don't scroll past row 2.
- Product detail with many alternate views: Hero image visible, 9 thumbnails below or in a swiper. Lazy load everything except hero.
- Blog posts with embedded images: Article-length pages with 5-10 inline images.
- Footer images: Logos, payment badges, social icons. Always lazy.
Where Lazy Loading Hurts
This is where teams routinely break their LCP score:
1. The hero / LCP image
Largest Contentful Paint is the time to render the largest image visible in the viewport on first paint. If you lazy load the hero, the browser waits to start fetching it until JavaScript runs or the intersection observer fires. That delay shows up directly as worse LCP.
Rule: The hero image and any image above the fold should never be lazy loaded. If anything, mark them fetchpriority="high" so the browser prioritizes them over below-fold content.
2. Product page main image on mobile
On desktop, the gallery thumbnails are next to the hero. On mobile, they're often below — so the hero IS above the fold and dominates first paint. If your template uses the same component for both and lazy loads everything, mobile LCP is the casualty.
3. Above-the-fold logos and badges
Trust badges in the header, payment icons in checkout, store logo. These are tiny but still LCP-eligible if larger than other elements. Don't lazy load them.
4. Images that are visible but in a hidden container
A common bug: a slider where slide 1 is visible but slides 2-10 are technically in the DOM. If the slider script loads slide 2's image only when the user advances, but you marked all images loading="lazy", slide 2 might not start loading until the user scrolls. The slider feels broken.
The Right Implementation
Native lazy loading (preferred)
Modern browsers all support loading="lazy". No JavaScript needed.
<img src="hero.webp" alt="Hero image" fetchpriority="high">
<img src="thumb-1.webp" alt="Thumbnail 1" loading="lazy">
<img src="thumb-2.webp" alt="Thumbnail 2" loading="lazy">
<img src="related-1.webp" alt="Related" loading="lazy">
The first image is the LCP candidate — high priority, no lazy. Everything below the fold gets loading="lazy". Done.
Add explicit width and height
Without intrinsic dimensions, lazy-loaded images cause layout shift when they finally load (CLS hit). Always specify width and height attributes — even if your CSS overrides them, the browser uses them to reserve space.
<img src="thumb.webp" alt="Product" loading="lazy" width="800" height="800">
Combine with responsive images
Lazy loading works with srcset — the browser still picks the right size, just defers the load.
<img
src="product-800.webp"
srcset="product-400.webp 400w, product-800.webp 800w, product-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 800px"
alt="Product"
loading="lazy"
width="800"
height="800"
>
The Common Mistakes
Marking everything lazy
Themes that "support lazy loading" often add loading="lazy" to every image. This destroys LCP. Audit your theme — there should be at least one image per page with fetchpriority="high" and no lazy attribute.
Using a JavaScript lazy loader on top of native
If you use both (native loading="lazy" AND a JS library that listens for scroll events), the JS library often holds the image in data-src and replaces with src on scroll. The browser's native lazy loading sees the placeholder, doesn't see the real image, and the script controls everything. This adds complexity without benefit. Pick one.
Lazy loading background images via CSS
CSS background images don't have a lazy attribute. If you must use background images, the only way to lazy load them is with JavaScript (intersection observer). For ecommerce, prefer <img> elements over CSS backgrounds — easier to optimize, accessible, indexable in image search.
Forgetting noscript fallbacks
If you use a JavaScript lazy loader (not native), include a <noscript> tag with the real image. Otherwise users with JS disabled and search engine crawlers may not see images at all.
Validating Your Setup
After implementing:
- Run Lighthouse on your top 3 page templates. LCP should be under 2.5s on mobile.
- Check the LCP element in Lighthouse — make sure it's the image you intended (usually the hero).
- Disable JavaScript and reload — images should still appear (otherwise SEO will suffer).
- Test in WebPageTest with throttled mobile. Watch the waterfall — the hero image should start loading immediately, not after JavaScript.
- Run our page weight tool on a category page before and after. Should see significant total bytes reduction with no LCP regression.
The Performance Impact
Median improvement on a 50-product category page after correct lazy loading setup:
- Page weight: -65% (only ~10 images load on initial render vs. all 50)
- Time to Interactive: -1.2s on 4G mobile
- LCP: usually unchanged (because hero wasn't lazy)
- CLS: occasionally worsens if width/height not specified — check this
The big win is bandwidth, not first paint. Done right, lazy loading is invisible to the user — they get the same fast first impression, and the browser quietly skips loading 80% of the page's image weight that they'd never have scrolled to anyway.
Done wrong, you slow down the most important moment on every page. Audit yours.