Lazy Loading Images and Videos: Complete Guide
Stop loading offscreen images upfront. Lazy loading cuts initial page weight by 40-70% and speeds up every single page load.
April 20, 2026 ยท SlapMyWeb Team

Lazy Loading Images and Videos: Complete Performance Guide
Bhai, tumhare homepage pe 47 images hain aur sab ki sab page load hote hi download ho rahi hain. User ne abhi sirf hero section dekha hai, lekin browser background mein footer ki stock photos bhi download kar raha hai. Internet bill toh user ka jal raha hai, lekin tumhari site ki speed bhi jal rahi hai. Yaar, 2026 mein agar tumne lazy loading images implement nahi kiya, toh tum apne users ke saath zulm kar rahe ho. Mobile pe 3G connection wala banda tumhare 15MB page load hone ka wait karega? Nahi karega โ wo back button maar ke competitor ke paas chala jayega. Google bhi dekhta hai ke tumhara LCP 8 seconds hai aur sochta hai "ye banda serious nahi hai." Core Web Vitals fail, bounce rate sky-high, ranking drop โ sab ek hi mistake ki wajah se. Aaj hum lazy loading ko properly implement karenge, har framework ke liye, har use case ke liye.

What Is Lazy Loading?
Lazy loading is a performance optimization technique where resources (images, videos, iframes) are loaded only when they're about to enter the viewport โ meaning when the user scrolls near them.
Without lazy loading, the browser downloads every single image on the page as soon as the HTML loads. On a page with 30 images, the browser fires 30 HTTP requests immediately, even for images at the very bottom that the user might never scroll to.
With lazy loading images, the browser only downloads what's visible (or about to be visible). Everything below the fold waits until the user scrolls down. This dramatically reduces initial page load time, bandwidth consumption, and server load.
The concept is simple: don't load what the user can't see yet.
Why Lazy Loading Matters for Performance and SEO
Lazy loading images directly impacts four critical performance metrics.
Faster initial page load. By deferring off-screen images, the browser can focus its bandwidth and processing on the content the user actually sees first. A page that would take 6 seconds to fully load might render the visible portion in 1.5 seconds with lazy loading.
Reduced bandwidth consumption. Users on mobile data plans shouldn't pay (literally) for images they'll never see. Studies show that the average user scrolls through about 60-70% of a page. That means 30-40% of eagerly loaded images were completely wasted bandwidth.
Better Largest Contentful Paint (LCP). LCP measures when the largest visible element finishes loading. When the browser isn't competing with 30 other image downloads, the hero image (your likely LCP element) loads faster. Google uses LCP as a Core Web Vitals ranking signal.
Improved Cumulative Layout Shift (CLS). Properly implemented lazy loading images with explicit width and height attributes prevent layout shifts as images pop into the page. The browser reserves the correct space before the image loads.
Run a free speed test on your site to see how many off-screen images are loading eagerly. You'll likely be surprised.
Native Lazy Loading: The Simplest Implementation
Modern browsers support native lazy loading with a single HTML attribute. No JavaScript required.
<!-- Native lazy loading โ supported in all modern browsers -->
<img
src="product-photo.webp"
alt="Red running shoes side view"
width="800"
height="600"
loading="lazy"
/>
<!-- Eager loading for above-fold images (explicit default) -->
<img
src="hero-banner.webp"
alt="Summer collection hero banner"
width="1200"
height="400"
loading="eager"
/>
<!-- Native lazy loading for iframes -->
<iframe
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
width="560"
height="315"
loading="lazy"
title="Product demo video"
></iframe>The loading="lazy" attribute tells the browser to defer loading until the element is near the viewport. The browser determines "near" based on network conditions and device type โ on slow connections, it starts loading earlier to compensate.
Browser support for native lazy loading is excellent: Chrome, Edge, Firefox, Safari, and Opera all support it. That covers 97%+ of global users. For the tiny percentage on older browsers, the image simply loads normally (eager) โ there's no broken experience.
Critical rule: always set `width` and `height` attributes on lazy-loaded images. Without explicit dimensions, the browser can't reserve space for the image before it loads, causing layout shifts (CLS problems) as images pop in.
JavaScript Intersection Observer API
For more control over lazy loading behavior, the Intersection Observer API gives you precise control over when resources load.
// Lazy loading with Intersection Observer
document.addEventListener('DOMContentLoaded', () => {
const lazyImages = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
// Swap data-src to src to trigger load
img.src = img.dataset.src;
// Load srcset if available (responsive images)
if (img.dataset.srcset) {
img.srcset = img.dataset.srcset;
}
img.classList.add('loaded');
observer.unobserve(img); // Stop watching once loaded
}
});
},
{
// Start loading 200px before the image enters viewport
rootMargin: '200px 0px',
threshold: 0.01,
}
);
lazyImages.forEach((img) => imageObserver.observe(img));
});The corresponding HTML uses data-src instead of src:
<!-- Image with Intersection Observer lazy loading -->
<img
data-src="product-photo.webp"
data-srcset="product-400.webp 400w, product-800.webp 800w"
alt="Blue wireless headphones"
width="800"
height="600"
class="lazy"
/>The rootMargin: '200px 0px' setting is important โ it tells the observer to start loading images 200px before they enter the viewport. This gives the browser a head start so images are typically loaded by the time the user scrolls to them, creating a seamless experience.
When to use Intersection Observer over native lazy loading:
- You need custom loading animations or fade-in effects
- You want to lazy load background images (CSS
background-image) - You need to track which images users actually view (analytics)
- You need precise control over the loading threshold distance
For most sites, native loading="lazy" is sufficient. Only reach for Intersection Observer when you need the extra control.

Lazy Loading Videos and Iframes
Videos are often the heaviest resources on a page. A single embedded YouTube video loads multiple scripts, stylesheets, and preview images โ adding 500KB+ to your page even if the user never clicks play.
YouTube/Vimeo embeds. Use loading="lazy" on the iframe, but go further โ replace the iframe with a thumbnail and play button, loading the actual embed only on click:
<!-- Lightweight YouTube facade โ loads embed only on click -->
<div class="video-facade" data-video-id="dQw4w9WgXcQ">
<img
src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"
alt="Product demo video thumbnail"
width="560"
height="315"
loading="lazy"
/>
<button class="play-button" aria-label="Play video">▶</button>
</div>
<script>
document.querySelectorAll('.video-facade').forEach((facade) => {
facade.addEventListener('click', () => {
const videoId = facade.dataset.videoId;
const iframe = document.createElement('iframe');
iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
iframe.width = 560;
iframe.height = 315;
iframe.allow = 'autoplay; encrypted-media';
iframe.allowFullscreen = true;
facade.replaceWith(iframe);
});
});
</script>This technique (called a "facade" or "lite embed") saves 500KB-1MB of initial page weight per video embed. Your page loads in a fraction of the time, and users who actually want to watch the video get it with a single click.
Self-hosted videos. Use the preload attribute:
<!-- Only load video metadata, not the full file -->
<video
src="demo.mp4"
preload="none"
poster="demo-poster.jpg"
controls
width="800"
height="450"
>
Your browser does not support the video tag.
</video>Setting preload="none" prevents the browser from downloading any video data until the user clicks play. preload="metadata" downloads just the duration and dimensions. Avoid preload="auto" unless the video is the primary content of the page.
Framework-Specific Lazy Loading
Every modern framework has built-in lazy loading support. Use it instead of reinventing the wheel.
Next.js (React). The next/image component lazy loads by default:
import Image from 'next/image';
// Automatically lazy loaded (default behavior)
<Image
src="/products/shoes.webp"
alt="Running shoes product photo"
width={800}
height={600}
/>
// Above-fold hero โ disable lazy loading
<Image
src="/hero-banner.webp"
alt="Summer sale hero banner"
width={1200}
height={400}
priority // Disables lazy loading, adds preload hint
/>Next.js priority prop is the equivalent of loading="eager" โ use it for your hero image and any above-fold visuals.
WordPress. Since WordPress 5.5, all images get loading="lazy" automatically. WordPress 5.9+ also skips lazy loading on the first image in post content (likely the LCP element). If you need more control, use the wp_lazy_loading_enabled filter.
React (vanilla). Use React.lazy() for component-level lazy loading and native loading="lazy" for images within those components.
When NOT to Lazy Load
This is where most developers mess up. Not everything should be lazy loaded.
Hero images and above-fold content. Your hero banner, logo, and any image visible without scrolling should load immediately. Lazy loading these causes a visible delay โ the user sees a blank space or placeholder before the image appears. This hurts both user experience and LCP scores.
LCP element. Whatever your Largest Contentful Paint element is (usually a hero image or large heading), it must NOT be lazy loaded. Adding loading="lazy" to your LCP element will tank your Core Web Vitals score because the browser deliberately delays loading it.
Critical background images. If a background image is part of the initial viewport, don't lazy load it. Use fetchpriority="high" instead.
Images inside carousels that auto-play. If a carousel auto-advances, the next slide's image needs to be loaded before it appears. Lazy loading here creates a jarring experience with images popping in mid-transition.
How do you know which images are above the fold? Run a SlapMyWeb scan โ our Performance pillar specifically identifies images that should and shouldn't be lazy loaded based on their viewport position.
Common Lazy Loading Mistakes
Lazy loading the hero image. This is the single most common lazy loading images mistake. Your hero is almost always the LCP element. Making it lazy guarantees a slow LCP score. Google will flag this in PageSpeed Insights and penalize your Core Web Vitals.
No width/height on lazy images. Without explicit dimensions, the browser allocates zero space for the image. When it loads, content jumps around (layout shift). Always set width and height attributes, or use CSS aspect-ratio.
Using JavaScript when native works. Loading a 30KB JavaScript lazy loading library when loading="lazy" does the same job for free. Unless you need Intersection Observer-level control, native is better in every way โ lighter, faster, and more reliable.
No fallback for JavaScript-disabled users. If you use JavaScript-based lazy loading with data-src, users with JS disabled see no images at all. Always include a <noscript> fallback:
<img data-src="photo.webp" alt="Product photo" class="lazy" />
<noscript>
<img src="photo.webp" alt="Product photo" />
</noscript>Not compressing images before lazy loading. Lazy loading defers WHEN images load, not HOW MUCH they weigh. A 5MB uncompressed PNG is still 5MB when it lazy loads. Compress and convert to WebP/AVIF first using an image compressor, then apply lazy loading on top.
CLS from placeholder to real image. If your lazy loading implementation uses a tiny placeholder that's a different aspect ratio than the final image, the page will shift when the real image loads. Match placeholder dimensions exactly to the final image dimensions.

Measuring Lazy Loading Impact
After implementing lazy loading images, verify the results.
Google PageSpeed Insights. Run your page through PageSpeed and look for the "Defer offscreen images" audit. If it's green, your implementation is working. Also check LCP โ it should improve unless you accidentally lazy-loaded the hero.
Chrome DevTools Network tab. Open DevTools, go to the Network tab, filter by "Img", and slowly scroll the page. You should see image requests firing as you scroll, not all at once on page load. If all images load immediately, your lazy loading isn't working.
WebPageTest. Run a test at webpagetest.org and compare the filmstrip view with and without lazy loading. The visual difference in initial load speed is often dramatic.
SlapMyWeb Performance Pillar. Our website speed test checks lazy loading implementation, identifies images that shouldn't be lazy loaded (LCP elements), and flags missing width/height attributes that cause CLS issues. You'll get specific code fixes for every issue found.
Quick Implementation Checklist
- Add
loading="lazy"to all<img>tags below the fold - Add
loading="lazy"to all<iframe>tags (YouTube, maps, widgets) - Set
widthandheighton every image element - Use
priority(Next.js) orloading="eager"on hero/LCP images - Replace YouTube embeds with facade pattern for heavy pages
- Set
preload="none"on self-hosted videos - Compress all images to WebP/AVIF before applying lazy loading
- Test with Chrome DevTools Network tab to verify deferred loading
- Run PageSpeed Insights to confirm LCP and CLS improvements
FAQ
Does lazy loading affect SEO or image indexing?
No. Googlebot executes JavaScript and scrolls pages, so it discovers lazy-loaded images just fine. Google has explicitly stated that loading="lazy" does not prevent images from being indexed. However, if you use a JavaScript-only approach without <noscript> fallbacks, some crawlers that don't execute JS might miss your images. Native loading="lazy" is always safe for SEO.
Should I lazy load images on a single-page app (SPA)?
Yes, but the approach differs. In SPAs, route changes don't trigger full page loads, so native loading="lazy" works within each view. For route-level lazy loading, use your framework's code splitting (React.lazy, Vue async components, Angular loadChildren) to defer entire components including their images until that route is accessed.
What's the difference between loading="lazy" and fetchpriority="high"?
They're opposite signals. loading="lazy" tells the browser to defer loading until the element is near the viewport. fetchpriority="high" tells the browser to prioritize this resource over others. Use fetchpriority="high" on your LCP/hero image to make it load faster, and loading="lazy" on everything below the fold. Never use both on the same element โ they contradict each other.
How much speed improvement can I expect from lazy loading?
It depends on how image-heavy your page is. Pages with 20+ images typically see 40-60% reduction in initial page weight and 1-3 second improvement in LCP. E-commerce product listing pages often see the biggest gains โ going from 8+ seconds to under 3 seconds on mobile. Pages with only 2-3 images won't see dramatic improvements since there's little to defer.
Ready to check your site? Run a free website audit and get a prioritized report with copy-paste code fixes in 30 seconds.