11 min read · Technical SEO · Last updated July 2026
Quick answer: Use native lazy loading (
loading="lazy") on all images except your LCP element (typically the hero image above the fold). Never lazy-load images visible in the initial viewport. For JavaScript-based lazy loading libraries, follow the same rule — exclude above-fold images. Done right, lazy loading reduces initial page weight by 40–70% on image-heavy pages.
Introduction
Lazy loading is one of the few optimizations that’s simple to implement, has near-universal browser support, and reliably improves page load metrics. The concept: don’t download images users haven’t scrolled to yet. Download them as they approach the viewport.
But there’s one critical mistake that turns this optimization into a Core Web Vitals failure: lazy loading your hero image. That image is your Largest Contentful Paint element on most pages. Adding loading="lazy" to it tells the browser to deprioritize it — which can push your LCP from 1.5 seconds to 4+ seconds.
This guide covers correct lazy loading implementation, LCP exclusion patterns, and how to measure the impact.
What you’ll learn:
– How native lazy loading works and its browser support status
– Which images must never be lazy-loaded
– How to identify your LCP element to ensure it’s excluded
– JavaScript lazy loading libraries and when you need them
– The impact of lazy loading on Core Web Vitals metrics
Table of Contents
- How Lazy Loading Works
- Native Lazy Loading Implementation
- Identifying and Excluding Your LCP Image
- JavaScript Lazy Loading Libraries
- Lazy Loading for iframes and Videos
- Intersection Observer API
- Impact on Core Web Vitals
- Frequently Asked Questions
- Conclusion
How Lazy Loading Works
Without lazy loading, a browser downloading a product page with 20 product images downloads all 20 images simultaneously during the initial page load — even if the user only sees 4 images before scrolling. This is wasteful: 16 images are downloaded that may never be viewed (average bounce rate for ecommerce: 45–65%).
Lazy loading defers image downloads until the image enters the viewport (or approaches it, within a configurable distance threshold). The browser loads images just before they become visible.
Browser-native mechanism: the browser tracks which elements are near the viewport using an internal intersection algorithm. When a lazy-loaded image enters the “loading distance” (typically 1,250px below the viewport on fast connections, closer on slow connections), the browser starts downloading it. The distance threshold is adaptive — Chromium adjusts it based on connection speed.
What happens before the image loads: the browser reserves space for the image based on its declared width and height attributes. Without these attributes, the space collapses to zero and expands when the image loads — causing CLS. Always declare dimensions on lazy-loaded images.
Native Lazy Loading Implementation
Native lazy loading requires one attribute: loading="lazy" on an <img> tag.
<!-- Standard implementation -->
<img
src="product-shoe.webp"
alt="Blue Nike running shoe, right side view"
width="600"
height="600"
loading="lazy"
>
That’s it. No JavaScript required. No library to load. No intersection observer to write.
Browser support: 96.5%+ as of 2026. Native lazy loading works across Chrome, Firefox, Safari, Edge, and all major mobile browsers. The only browsers that don’t support it: IE11 and extremely old mobile browsers. For these, the image simply loads normally (no JavaScript fallback needed — the loading attribute is silently ignored by unsupporting browsers).
Additional best practices with lazy loading:
– Always include width and height — prevents CLS from dimension-unknown images
– Include descriptive alt text — for accessibility and SEO
– Use srcset for responsive images — lazy loading and responsive images work together seamlessly
<img
srcset="product-400.webp 400w, product-800.webp 800w"
sizes="(max-width: 600px) 100vw, 50vw"
src="product-800.webp"
alt="Blue Nike running shoe, right side view"
width="800"
height="800"
loading="lazy"
>
Native vs JavaScript Lazy Loading
Toggle between implementation patterns and see the code for each approach.
<!-- ✅ DO: Lazy load below-fold images --> <img src="product.webp" alt="Product description" width="800" height="600" loading="lazy" > <!-- ❌ NEVER: Lazy load your LCP/hero image --> <!-- This kills your LCP score: --> <img src="hero.webp" alt="Hero image" loading="lazy" ← REMOVE THIS from hero images > <!-- ✅ DO: Hero image should use eager loading + preload --> <link rel="preload" as="image" href="hero.webp" type="image/webp"> <img src="hero.webp" alt="Hero image" width="1200" height="600" loading="eager" fetchpriority="high" >
Identifying and Excluding Your LCP Image
The most critical lazy loading rule: never lazy-load your LCP element.
Identifying your LCP element:
- Open Chrome DevTools → Performance tab
- Record a page load, then look at the “Timings” section in the flame graph
- The LCP marker shows which element triggered LCP — hover over it to see the element
Alternatively, use PageSpeed Insights. Under “Diagnostics,” the “Largest Contentful Paint element” section identifies the exact DOM element that triggered your LCP.
Common LCP elements by page type:
| Page Type | Typical LCP Element |
|---|---|
| Homepage | Hero image (banner, feature image) |
| Blog post | Feature/thumbnail image at top |
| Product page | Primary product image |
| Category page | First product grid image or category banner |
| Landing page | Above-fold background or illustration |
For text-based LCP: if your LCP element is a heading (<h1>) or paragraph, lazy loading doesn’t apply (you can’t lazy-load text). In this case, focus on ensuring the web font used for that text loads as fast as possible.
LCP Exclusion Checker
Answer questions about each image on your page to determine the correct loading strategy.
JavaScript Lazy Loading Libraries
For most sites in 2026, native lazy loading is sufficient. But there are scenarios where JavaScript-based lazy loading provides capabilities native loading doesn’t:
When to consider JS lazy loading:
– Background images in CSS (background-image: url(...)) — native loading="lazy" only works on <img> tags, not CSS backgrounds
– Custom loading animations (blur-up technique, LQIP — low quality image placeholders)
– Complex media galleries where you want scroll-triggered animations alongside loading
– Sites requiring fine-grained control over loading distance threshold
Popular libraries:
– Lozad.js (569 bytes minified): Intersection Observer based, no dependencies
– Vanilla-lazyload (3.5KB): More features, custom callbacks, supports CSS backgrounds
– lazysizes (3.4KB): Most popular, handles responsive images and LZID pattern
For CSS background images specifically:
Lazy Loading for iframes and Videos
Native lazy loading also works on <iframe> elements — including embedded YouTube videos and Google Maps:
<!-- Lazy-loaded YouTube embed -->
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
loading="lazy"
width="560"
height="315"
title="Video title"
allowfullscreen
></iframe>
<!-- Lazy-loaded Google Map -->
<iframe
src="https://www.google.com/maps/embed?..."
loading="lazy"
width="600"
height="450"
title="Our location"
></iframe>
YouTube embeds are one of the highest-impact candidates for lazy loading. The YouTube player JavaScript is substantial — loading it eagerly for a video below the fold adds 400–600KB to initial page weight. With lazy loading, this only downloads when the user scrolls to the video.
YouTube facade pattern: for even better performance, use a lightweight facade (thumbnail image + play button) that loads the real YouTube player only when clicked:
This pattern can save 400–600KB of JavaScript on page load for pages with embedded videos.
Impact on Core Web Vitals
Lazy loading’s CWV impact depends heavily on correct implementation:
LCP: correctly excluding the LCP image from lazy loading (and preloading it) can improve LCP by 0.5–1.5 seconds. Incorrectly lazy-loading the LCP image can degrade LCP by the same margin. This is a high-stakes decision.
CLS: lazy-loaded images without width and height attributes cause significant CLS. When the image loads, it pushes content below it down. Always specify dimensions. For responsive images where you can’t know the exact dimensions, use aspect-ratio CSS:
img.lazy {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
}
INP: lazy loading reduces initial JavaScript execution, which can improve INP by reducing main thread contention during page load. Scripts and images competing for network and CPU during load create the background processing that delays interaction responses.
CWV in numbers: a WooCommerce category page with 48 products reduced initial page weight from 4.2MB to 1.1MB by implementing native lazy loading on all product images (keeping the first 4 product images eager-loaded as potential LCP candidates). LCP improved from 3.2s to 1.8s.
Frequently Asked Questions
Does lazy loading affect SEO and image indexing?
Google says it can crawl and index lazy-loaded images. However, Google uses the loading attribute as a signal — it respects loading="lazy" and may not crawl those images on first visit, indexing them in a “second wave.” For images you want indexed (product images, infographics, feature images), use loading="eager" or omit the attribute.
How many images should be eager-loaded above the fold?
The first 1–4 images visible on initial viewport render should be eager-loaded. For a product category page showing a 4-column grid, the first row (4 images) should be eager; all subsequent rows should be lazy. For mobile, where layout may show fewer images above the fold, 1–2 images may be sufficient.
Does native lazy loading work with WordPress?
WordPress added loading="lazy" to all images in version 5.5 (August 2020). By default, WordPress automatically applies lazy loading to all images inserted via the editor and the_post_thumbnail() functions. You need to manually exclude the featured image or hero image from lazy loading if WordPress is applying it.
Can lazy loading hurt crawlability?
For standard <img> tags with loading="lazy", Googlebot handles lazy loading correctly in most cases. Issues arise with JavaScript-dependent lazy loading where images only load after JS execution. If your lazy loading requires JavaScript and Googlebot doesn’t execute the JS, images may not be crawled. Use native lazy loading to avoid this risk.
What’s fetchpriority="high" and should I use it?
fetchpriority="high" tells the browser to prioritize a resource in the download queue. Add it to your LCP image to ensure the browser downloads it before lower-priority assets. Supported in Chrome 101+, Safari 17.2+. For older browsers, the attribute is silently ignored.
Conclusion
Lazy loading is simple, high-impact, and nearly universally supported. Add loading="lazy" to every image except your LCP element, always include width and height attributes, and preload your hero image with fetchpriority="high".
The implementation takes 20 minutes on most platforms. The Core Web Vitals impact — particularly for image-heavy pages like product catalogs and blog archives — can be a 30–50% reduction in initial page weight and a 0.5–1.5 second LCP improvement.
One rule to internalize: the hero image loads fast, everything else loads late.
Let Ignited Nepal Handle This
→ Request a Free Technical SEO Audit
Written by the Ignited Nepal SEO team. We build organic search systems for businesses across Nepal, Australia, UAE, USA, UK, and beyond. ignitednepal.com