Lazy loading offscreen images while protecting Core Web Vitals and Google indexing

Lazy Loading Images for SEO: The Complete Guide for E-commerce

15 min read
Lazy LoadingImage SEOCore Web VitalsLCPPage SpeedShopifyWooCommerceNext.js

Your PageSpeed report says "defer offscreen images," so you turn on lazy loading. Then a worry creeps in: if the browser waits to load images, does Google still see them? And will deferring images make your Core Web Vitals worse, not better?

Both fears are reasonable. Both are also fixable with a single rule that most store owners get backwards. Lazy loading is safe for SEO and great for speed — right up until you apply it to the one image that shouldn't be lazy loaded.

This guide covers what lazy loading actually does, what Google says about indexing, and the exact pattern that clears "defer offscreen images" without hurting your Largest Contentful Paint. No code background assumed. It fits alongside the other layers in our complete image SEO guide.

Key Takeaways

  • Lazy loading does not hurt image SEO when done right. Google indexes lazy-loaded images as long as they load when they enter the viewport, not on a click or scroll (Google Search Central).
  • The real trap is lazy loading your above-the-fold hero image. Images are the Largest Contentful Paint element on 83% of desktop pages and 73% of mobile pages (Web Almanac 2025).
  • The correct pattern: fetchpriority="high" on the LCP image, loading="lazy" on everything below the fold. Adding fetchpriority="high" to an LCP image improved it by 700ms in one real test (web.dev).
  • Native loading="lazy" works in roughly 96% of browsers and needs zero JavaScript — the right default for nearly every store.

What Lazy Loading Actually Does

Lazy loading tells the browser to hold off downloading an image until the visitor is about to see it. Images near the top load right away. Images further down wait until the reader scrolls close to them.

Think of a long product page with forty photos. Without lazy loading, the browser fetches all forty the instant the page opens — even the ones the visitor may never scroll to. That's wasted bandwidth and a slower start. Lazy loading loads the first few, then fetches the rest on demand.

There are two ways to do this, and the difference matters for both speed and SEO:

  • Browser-level (native) lazy loading. You add one attribute — loading="lazy" — to an image tag. The browser handles the rest. No scripts, no libraries, nothing to break.
  • JavaScript libraries. Tools like lazysizes or lozad watch the page with code and swap in images as they approach the viewport. More control, more moving parts.

For years, JavaScript libraries were the only option. That changed when browsers built lazy loading in directly. Today the native attribute is supported by around 96% of global browsers, according to Can I Use. For most stores, the one-line native version is all you need.

Does Lazy Loading Hurt SEO? What Google Actually Says

No — lazy loading does not hurt image SEO when it's implemented correctly. This is the myth worth killing first, because it stops store owners from fixing a real speed problem.

Google's own documentation is direct about it. Google supports lazy loading and indexes lazy-loaded images, and it explicitly recommends three methods: native browser lazy loading, the IntersectionObserver API, and JavaScript libraries that load content as it enters the viewport (Google Search Central).

Here's the one condition that decides everything. Your images must load when they become visible in the viewport — not when the user scrolls or clicks. Google's crawler renders your page in a single pass. It does not scroll, tap, or interact. So if your lazy loading only triggers on a real scroll event, Googlebot never fires it, and the image stays invisible to search.

That's the entire risk. It's not lazy loading that hurts SEO — it's lazy loading that hides images behind an action the crawler won't perform. Native loading="lazy" avoids the trap by design, because the browser loads the image based on position, not user behavior.

If your images already sit in an image sitemap, you've given Google a second path to discover them regardless — a useful backstop while you verify your setup renders cleanly.

Native vs IntersectionObserver vs Library: When Each Makes Sense

Three methods, one recommendation for most stores, and clear cases where the other two earn their place. Here's how they compare.

| Method | How it works | Best for | Trade-off | |--------|-------------|----------|-----------| | Native loading="lazy" | Browser attribute, no code | Almost every store | Less control over placeholders | | IntersectionObserver | A browser API driven by your own JavaScript | Custom triggers, background images | You write and maintain the code | | Library (lazysizes, lozad) | Prebuilt JavaScript that manages loading | Legacy browser support, complex galleries | Extra script to load and update |

Native loading="lazy" is the default answer. It's one attribute, it ships with the browser, and it's SEO-safe because the browser — not a scroll listener — decides when to load. Add loading="lazy" to below-the-fold images and you're done.

IntersectionObserver makes sense when you need control the attribute can't give you: lazy loading CSS background images, triggering an animation as an image appears, or a custom low-quality placeholder. It's a browser API, so it's reliable — but you own the code.

A library makes sense in narrower cases: you must support very old browsers, or you have a complex gallery where a battle-tested tool saves you time. The cost is another JavaScript file to load, and one more thing that can conflict with your theme.

For a typical Shopify, WooCommerce, or custom store, native lazy loading covers 90% of needs with none of the risk. Reach for the others only when you hit a wall the attribute can't clear.

The LCP Trap: Why Lazy Loading Your Hero Image Kills Core Web Vitals

Here's where good intentions backfire. Largest Contentful Paint (LCP) measures how long it takes for the biggest visible element to render. Google wants it under 2.5 seconds, and it's one of the three Core Web Vitals that feed into rankings.

On e-commerce pages, that "biggest visible element" is almost always an image. The Web Almanac's 2025 analysis found images are the LCP element on 83% of desktop pages and 73% of mobile pages (Web Almanac 2025). For a product page, it's usually your main product photo or hero banner.

Now the trap. If you lazy load every image — including that hero shot — you've told the browser to delay the single most important pixel on the page. The browser first has to lay out the page, discover the image is near the viewport, then start downloading it. That detour adds delay to the exact metric you're trying to improve.

Lazy loading your LCP image is one of the most common self-inflicted Core Web Vitals wounds. You turned on lazy loading to speed things up, and instead you slowed down the one measurement Google watches.

How to find your LCP element

You can't fix what you can't see. Two free ways to identify it:

  • PageSpeed Insights. Run your product URL. Under diagnostics, it names the LCP element directly.
  • Chrome DevTools. Open the Performance panel, record a page load, and find the LCP marker on the timeline. It points to the exact element.

Nine times out of ten, it's the top product image. That's the one image you must not lazy load.

The Correct Pattern: fetchpriority="high" for the LCP Image, loading="lazy" for the Rest

The fix is a two-part rule, and it resolves the whole conflict between speed and Core Web Vitals. Load your LCP image eagerly and with high priority; lazy load everything below the fold.

In practice, that means two things on your page:

  1. The LCP image (your hero or main product photo) gets fetchpriority="high" and no loading="lazy". This tells the browser to fetch it first, before it has even finished computing the layout.
  2. Every other image below the fold gets loading="lazy". Thumbnails, related products, footer graphics, review photos — all deferred.

The fetchpriority="high" attribute is the newer half of this pattern, and it's powerful. In one real test on web.dev, simply adding it to the LCP image improved LCP by 700 milliseconds — from 2.6 seconds down to 1.9 seconds, moving the page from failing to passing (web.dev).

One warning that comes straight from Google's guidance: don't mark everything high priority. As web.dev puts it, "if everything is high priority, nothing is." Use fetchpriority="high" on the one or two most important elements — the LCP image — and let loading="lazy" handle the rest.

That single pattern is what clears the "defer offscreen images" flag and protects your LCP at the same time. This is the same speed-plus-SEO balance we cover in how to optimize images for SEO and website speed.

How to Set It Up on Your Platform

Most platforms handle a lot of this automatically now. Your job is to know what yours does by default, then verify it — not to trust it blindly.

Shopify

Modern Shopify themes lazy load images out of the box. Theme code typically adds loading="lazy" to product and collection images, while marking the first image or hero as eager. The Online Store 2.0 free themes do this well.

Your job is verification. View a product page's source and confirm the main product image does not have loading="lazy", while gallery thumbnails do. If your theme lazy loads the hero, most themes expose a "preload" or "eager loading" toggle for the featured image in theme settings — or the fix is a small edit to the image snippet's Liquid, swapping loading="lazy" for the priority attributes on the first image.

WooCommerce

WordPress added native lazy loading by default back in version 5.5. But early on, it lazy loaded everything — including above-the-fold images. That hurt LCP, and WordPress spent several releases fixing it.

WordPress 5.9 began skipping lazy loading on the first content image, and version 6.3 finished the job: it now automatically adds fetchpriority="high" to the image it identifies as the likely LCP element, which the WordPress team estimated improves LCP by around 5–10% (Search Engine Journal).

So on a current WooCommerce store, the core behavior is usually correct. The most common problem is a plugin conflict: an image-optimization or lazy-load plugin (or a caching suite) that overrides WordPress and re-lazy-loads the hero. If your LCP is poor, deactivate lazy-load plugins one at a time and re-test — the culprit is often a helper you installed to improve speed.

Custom Next.js

If you're on Next.js, the next/image component handles this for you — once you flag the LCP image. By default, next/image lazy loads every image automatically.

The one thing you must add is the priority prop on your LCP image:

// Hero / main product image — loads eagerly, high priority
<Image src="/products/hammered-silver-ring.jpg" alt="Hammered sterling silver ring" width={1200} height={1200} priority />

// Gallery thumbnails — lazy loaded by default, no prop needed
<Image src="/products/ring-detail-1.jpg" alt="Ring band detail" width={400} height={400} />

The priority prop does two things at once: it disables lazy loading for that image and adds fetchpriority="high". That's the entire pattern in a single word. Set it on the one hero image per page — and only that one.

Common Lazy Loading Mistakes That Cost You Traffic

When lazy loading goes wrong, it's usually one of these four. Each is easy to spot once you know to look.

1. Lazy loading everything, including the LCP image. The most common mistake, covered above. Deferring your hero image delays LCP and can tip a passing page into failing. Always eager-load the one image visible on load.

2. Missing width and height attributes. Lazy-loaded images that appear without reserved space push content around as they load. That's Cumulative Layout Shift (CLS), another Core Web Vital. Always set explicit width and height (or an aspect ratio) so the browser reserves the slot before the image arrives.

3. JavaScript lazy loading that blocks crawlers. If your lazy loading only fires on a real scroll or click, Googlebot won't trigger it, and your images won't get indexed. This is the SEO risk from earlier. Native loading="lazy" and viewport-based IntersectionObserver avoid it; scroll-event-only scripts don't.

4. The wrong placeholder strategy. Some setups put a data-src on the image and leave src empty or pointing at a 1×1 pixel, filling the real URL in with JavaScript later. If that swap depends on interaction, Google may only ever see the placeholder. Make sure the real image URL ends up in the src attribute of the rendered HTML.

That last point ties into how your images get discovered and described in the first place — filenames, alt text, and product image schema all still have to be correct for a lazy-loaded image to rank once Google fetches it.

How to Verify It's Working

Don't assume — measure. Three free tools answer three different questions, and you want all three to agree.

PageSpeed Insights answers: is my speed problem solved? Run a product URL. The "defer offscreen images" opportunity should be gone or shrunk, and your LCP element should be loading fast. This is your headline check.

Chrome DevTools Network tab answers: what's loading, and when? Open the Network panel, filter to images, and reload. Above-the-fold images should appear at the top of the waterfall. Below-the-fold images should only load as you scroll — proof lazy loading is active. Your LCP image should be one of the first requests, not a late one.

Search Console answers: does Google see the images, and are Core Web Vitals passing? The Core Web Vitals report shows your LCP status across real visits over time. And the URL Inspection tool lets you render a page as Google does — check that your image URLs appear in the src of the rendered HTML. If they do, Google can index them.

Run PageSpeed and DevTools while you're setting things up. Watch Search Console over the following weeks, especially after theme or plugin updates — that's when working lazy loading quietly breaks.

Your Lazy Loading Checklist

Run any product page through this before you move on:

  • [ ] The LCP image (hero / main product photo) has no loading="lazy"
  • [ ] The LCP image has fetchpriority="high" (or priority in Next.js)
  • [ ] All below-the-fold images have loading="lazy"
  • [ ] Every image has explicit width and height to prevent layout shift
  • [ ] Lazy loading is native or viewport-based — not scroll/click-triggered
  • [ ] The real image URL appears in the rendered HTML src (check URL Inspection)
  • [ ] PageSpeed Insights no longer flags "defer offscreen images"
  • [ ] Search Console Core Web Vitals shows LCP passing over time

Frequently Asked Questions

Does lazy loading hurt SEO?

No — when it's implemented correctly. Google supports lazy loading and indexes lazy-loaded images, as long as they load when they enter the viewport rather than on a click or scroll event. Google's guidance names native loading="lazy" and IntersectionObserver as safe methods. The damage comes from bad implementations that hide images from Googlebot, not from lazy loading itself.

Should product gallery images be lazy loaded?

Mostly yes, with one exception. The first gallery image — the main product photo visible on load — is usually your LCP element and should load eagerly with fetchpriority="high". The remaining thumbnails and zoomed views sit below or behind the fold, so lazy loading them is exactly right. Lazy load the gallery; eager-load the hero shot.

Does lazy loading images improve page speed?

Yes, for images below the fold. Lazy loading defers offscreen images so the browser spends its initial bandwidth on what the visitor sees first. That's why PageSpeed flags "defer offscreen images." The catch: lazy loading your above-the-fold hero image does the opposite, delaying your Largest Contentful Paint instead of helping it.

Is native lazy loading better than a JavaScript plugin?

For most stores, yes. Native loading="lazy" is built into the browser, adds zero JavaScript, and works in roughly 96% of global browsers per Can I Use. A JavaScript library only earns its place when you need control — custom placeholders, background images, or old browser support — that the native attribute can't provide.

How do I know which image is my LCP element?

Open Chrome DevTools, go to the Performance panel, record a page load, and find the LCP marker on the timeline — it names the exact element. PageSpeed Insights also reports your LCP element under diagnostics. On most e-commerce pages it's the main product photo or the hero banner near the top.

The Bottom Line

Lazy loading is safe for SEO and genuinely good for speed. The single rule to remember: defer everything below the fold, but never the image visible when the page loads. Give that one hero image fetchpriority="high", lazy load the rest, and you clear "defer offscreen images" without touching your Core Web Vitals.

Once lazy loading is right, the images still have to earn their rankings — and that comes down to filenames, alt text, and embedded metadata. That's the layer ImgSEO automates: AI-generated alt text, keyword-rich filenames, and SEO metadata embedded into your product images, processed in batches. You can try it free on 30 images, no credit card required.

Load the hero fast. Lazy load the rest. Optimize every pixel Google finds. That's the full stack for turning product photos into Google traffic.

Share:
ImgSEO

Joseph

The team behind ImgSEO.io. We help online sellers optimize product images, improve search visibility, and create a better shopping experience across e-commerce platforms.

Optimize your product images with AI

Generate SEO titles, alt text, tags, filenames, and metadata in seconds.


Related Articles