Loading all images at once can slow down your app, especially on image-heavy pages. Lazy loading helps by deferring the loading of images until they’re actually visible in the viewport. This optimization improves performance, especially for users on slow networks or mobile devices.
Let’s explore how to implement lazy loading in React using modern and easy-to-use techniques.
🚀 What is lazy loading?
Lazy loading means delaying the loading of non-critical resources—like images—until the user needs them. For images, it means loading them only when they appear in the viewport (i.e., when the user scrolls to them).
Benefits:
- Faster initial page load
- Less bandwidth usage
- Better user experience
🧰 Option 1: native HTML loading="lazy"
Modern browsers support native lazy loading using the loading
attribute:
<img src="photo.jpg" alt="Description" loading="lazy" />
This works out of the box in most browsers and is perfect for simple use cases. But if you need more control, use the next approach.
🧠 Option 2: lazy loading with intersectionobserver
You can use the IntersectionObserver
API to detect when an image enters the viewport and load it dynamically.
Here’s a basic custom LazyImage
component:
import React, { useRef, useState, useEffect } from 'react';
const LazyImage = ({ src, alt, ...props }) => {
const [isVisible, setIsVisible] = useState(false);
const imageRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect(); // Stop observing after loading
}
},
{
threshold: 0.1,
}
);
if (imageRef.current) {
observer.observe(imageRef.current);
}
return () => observer.disconnect();
}, []);
return (
<imgref={imageRef}
src={isVisible ? src : ''}
alt={alt}
{...props}
/>
);
};
export default LazyImage;
Usage
<LazyImage src="/images/large-photo.jpg" alt="A beautiful landscape" />
This way, images won’t load until they are in (or close to) view.
✨ Optional: add a placeholder or blur effect
To enhance UX, show a low-quality placeholder or a blurred version before the actual image loads.
Here’s a quick extension to the LazyImage
component:
return (
<imgref={imageRef}
src={isVisible ? src : '/images/placeholder.jpg'}
alt={alt}
className={isVisible ? 'fade-in' : 'blur'}
{...props}
/>
);
And in your CSS:
.blur {
filter: blur(10px);
transition: filter 0.3s ease-out;
}
.fade-in {
filter: none;
}
🧱 Alternative: use libraries
If you prefer not to reinvent the wheel, try these libraries:
Example with react-lazy-load-image-component
:
npm install react-lazy-load-image-component
import { LazyLoadImage } from 'react-lazy-load-image-component';
<LazyLoadImagesrc="photo.jpg"
alt="Lazy loaded"
effect="blur"
/>
It adds built-in effects and handles observer setup internally.
✅ Summary
Technique | Pros | Best For |
loading="lazy" | Simple and native | Basic use cases |
IntersectionObserver | Fine-grained control | Custom UX, animations, fallback |
react-lazy-load-image-component | Feature-rich and easy to use | Production apps with many images |
📌 Conclusion
Implementing lazy loading in React is a small change that can deliver big performance gains. Whether you go with the native loading
attribute or a custom solution using IntersectionObserver
, your users will benefit from faster load times and smoother experiences.