Lazy loading is a one-line HTML attribute that defers offscreen images so the browser doesn't fetch 40 photos when a user has only scrolled past 3. Done right, it cuts initial page weight dramatically. Done wrong — lazy-loading the hero image, skipping width/height — it actively hurts your Core Web Vitals instead of helping them.
- ✓The native loading="lazy" attribute is supported by every major browser now — you don't need a JavaScript library for the basic case
- ✓Never lazy-load your hero image or anything above the fold — it delays the exact image Google measures for LCP
- ✓Lazy loading without explicit width/height attributes causes layout shift, which tanks your CLS score
- ✓Compress images before you lazy-load them, not instead of — lazy loading delays a request, it doesn't shrink the file

Lazy loading defers the browser's request for an image until that image is about to enter the viewport. Instead of downloading every photo on a page the instant it loads, the browser fetches only what's visible (or close to visible) and grabs the rest as the user scrolls. On an image-heavy page — a product grid, a photo gallery, a long blog post — that routinely cuts initial page weight by 40-70%.
It's also one of the easiest performance wins available, and one of the most commonly half-implemented. Get it right and your Largest Contentful Paint improves because the browser isn't competing for bandwidth across 40 images at once. Get it wrong — lazy-load the hero image, skip the width and height attributes — and you actively make your Core Web Vitals worse while thinking you've optimized something.
Why This Still Matters in 2026
Median page weight on e-commerce and content sites has kept climbing even as connection speeds improve, because cameras keep getting better and nobody resizes their photos before uploading. A product page with 12 images at 2-4MB each is a 30-40MB page load if every image fires on initial render. Google's Core Web Vitals — specifically Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS) — are now ranking factors, and both are directly affected by how images load.
A test page with 24 product images, all eager-loaded, took 4.8 seconds to reach LCP on a throttled 4G connection. The same page with everything below the fold lazy-loaded hit LCP at 1.6 seconds — the browser simply wasn't competing for bandwidth across two dozen images it didn't need yet.
The Native Implementation
As of 2026, every major browser supports the loading attribute natively. No library, no JavaScript, no polyfill required for the standard case.
<img src="product-photo.jpg" loading="lazy" width="800" height="600" alt="Blue ceramic vase on a white background">
- Add
loading="lazy"to every<img>tag below the fold. That's the entire native implementation — the browser handles the threshold, the intersection detection, and the fetch timing. - Always include
widthandheightattributes (or a CSSaspect-ratio). The browser needs these to reserve space for the image before it loads, otherwise the page jumps around as images pop in — that's exactly what CLS measures and penalizes. - Leave the hero image and anything above the fold as
loading="eager"(the default — you don't need to write it). Better still, addfetchpriority="high"to your actual LCP image so the browser prioritizes it over everything else competing for bandwidth. - Test with Chrome DevTools' Network tab — throttle to "Fast 3G," reload, and confirm only above-the-fold images fire immediately while the rest load in as you scroll.
When You Need a JavaScript Library Instead
Native lazy loading covers <img> tags fine, but it doesn't handle CSS background images, and the browser's built-in threshold (roughly 1-2 viewport heights ahead) isn't configurable. If you need either of those, lazysizes is still the standard choice — it uses the Intersection Observer API, supports background images via a class-based syntax, and lets you tune the load threshold and add fade-in transitions.
| Feature | Native loading="lazy" | lazysizes |
|---|---|---|
| Setup | One HTML attribute | Script tag + markup changes |
| Background images | Not supported | Supported |
| Configurable threshold | No | Yes |
| Bundle size | 0 KB | ~3 KB gzipped |
| Browser support (2026) | All major browsers | All browsers (polyfills the gap) |
For the vast majority of sites — blogs, product catalogs, photo galleries — native lazy loading is the right call. Reach for a library only when you specifically need background-image lazy loading or fine-grained threshold control.
The Mistakes That Quietly Break It
- Lazy-loading the hero image. This is the single most common mistake and it directly hurts LCP, because you've told the browser to deprioritize the exact image Google is measuring. Anything visible on initial load should be eager.
- No
width/heightattributes. Without them, the browser doesn't know how much space to reserve, so the layout jumps as each image loads in — a CLS penalty you created while trying to fix a different metric. - Treating lazy loading as a substitute for compression. Lazy loading delays when a request fires. It does nothing to the file size of that request. A 6MB uncompressed photo that loads lazily is still a 6MB photo the moment it does load — compress it or convert it to WebP first, then lazy-load the smaller version.
- Lazy-loading images inside a carousel's first slide. The first slide of any carousel is effectively above the fold the moment the page renders — treat it accordingly.
What to Do This Week
- Audit your site's image tags — anything below the fold without
loading="lazy"is wasted bandwidth on first load. - Add explicit
width/heightto every image tag that's missing them. - Run your homepage and your busiest product/gallery page through PageSpeed Insights before and after — you should see LCP and total page weight both drop.
- Before you tag images for lazy loading, run them through Optimage /compress first — lazy loading and compression solve different halves of the same problem, and you want both.
Related reading:
- Why Your LCP Is Failing and How to Fix It — the metric lazy loading most directly affects, and the rest of the fixes that go with it
- Google INP and Core Web Vitals: A 2026 Image Guide — for the interactivity metric lazy loading doesn't touch
- Why Your Website Is Slow in Nigeria and Africa — lazy loading matters most exactly where bandwidth is most constrained
Frequently asked questions
Does lazy loading hurt SEO?
No, as long as it's implemented correctly. Googlebot executes JavaScript and respects the native loading="lazy" attribute, so lazy-loaded images still get crawled and indexed. The SEO damage comes from lazy-loading the wrong images — specifically the hero image or anything above the fold, which delays your Largest Contentful Paint score.
Should I lazy-load every image on the page?
No. Lazy-load everything below the fold — that's the entire point. Never lazy-load the hero image, the first product photo, or any image visible without scrolling. Loading those eagerly (or with fetchpriority="high") is what keeps your LCP score fast.
What's the difference between native lazy loading and a JavaScript library like lazysizes?
Native loading="lazy" is a single HTML attribute, has zero JavaScript overhead, and is supported by Chrome, Firefox, Safari, and Edge as of 2026. A JS library like lazysizes adds more control — custom thresholds, fade-in effects, support for background images — at the cost of an extra script to load and maintain. For most sites, native lazy loading covers the use case without the dependency.
Continue reading
Try Optimage — it's free
Compress, convert, and optimize images in seconds. No sign-up, no limits.
Start Optimizing Free