When I first started optimizing my React applications, one of the biggest performance issues I noticed was slow image loading, especially when working on U.S.-based eCommerce dashboards that displayed hundreds of product images.
That’s when I discovered lazy loading. It completely changed how I approached image-heavy React apps. In this tutorial, I’ll show you exactly how to lazy-load images in React using the react-lazy-load-image-component library.
I’ll also share a few different methods that I’ve personally used in production apps over the years.
What is Lazy Loading in React?
In simple terms, lazy loading means loading content only when it’s needed. Instead of downloading all images right away, we load them as the user scrolls and they become visible in the viewport.
This approach makes web pages faster, improves user experience, and saves bandwidth, something that’s especially important for users browsing from mobile devices or slower networks.
Method 1 – Use the react-lazy-load-image-component Library
This is my go-to approach when I need a quick, reliable solution. The react-lazy-load-image-component library is lightweight and easy to use. It supports placeholder effects like blur and opacity transitions.
Step 1: Install the Library
Open your terminal and run:
npm install react-lazy-load-image-componentor, if you’re using Yarn:
yarn add react-lazy-load-image-componentStep 2: Import and Use the Component
Here’s a full working example that you can copy and run in your React project.
import React from "react";
import { LazyLoadImage } from "react-lazy-load-image-component";
import "react-lazy-load-image-component/src/effects/blur.css";
const ProductGallery = () => {
const products = [
{
id: 1,
name: "California Avocado Toast",
image: "https://example.com/images/avocado-toast.jpg",
price: "$12.99",
},
{
id: 2,
name: "New York Cheesecake",
image: "https://example.com/images/cheesecake.jpg",
price: "$8.99",
},
{
id: 3,
name: "Texas BBQ Burger",
image: "https://example.com/images/bbq-burger.jpg",
price: "$14.99",
},
];
return (
<div style={{ display: "flex", justifyContent: "space-around", flexWrap: "wrap" }}>
{products.map((product) => (
<div key={product.id} style={{ textAlign: "center", margin: "20px" }}>
<LazyLoadImage
alt={product.name}
src={product.image}
effect="blur"
height="200px"
width="250px"
style={{ borderRadius: "10px" }}
/>
<h4>{product.name}</h4>
<p>{product.price}</p>
</div>
))}
</div>
);
};
export default ProductGallery;In this example, the images load only when they appear in the viewport. The effect=”blur” adds a nice blur transition while the image loads.
Step 3: Add Placeholder or Custom Effects
The library allows you to add effects like blur, opacity, or even your own custom placeholders.
For instance:
<LazyLoadImage
alt="California Avocado Toast"
src="https://example.com/images/avocado-toast.jpg"
placeholderSrc="https://example.com/images/placeholder.jpg"
effect="opacity"
/>I executed the above example code and added the screenshot below.

This displays a placeholder image until the main image fully loads.
Method 2 – Lazy Loading Using the Intersection Observer API
If you prefer not to use an external library, you can achieve the same effect using the Intersection Observer API. This is a native browser feature that detects when an element enters the viewport.
Here’s how I’ve implemented it in some of my client projects.
import React, { useEffect, useRef, useState } from "react";
const LazyImage = ({ src, alt }) => {
const [isVisible, setIsVisible] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1 }
);
if (imgRef.current) observer.observe(imgRef.current);
return () => observer.disconnect();
}, []);
return (
<img
ref={imgRef}
src={isVisible ? src : ""}
alt={alt}
style={{ width: "250px", height: "200px", borderRadius: "10px" }}
/>
);
};
const App = () => {
return (
<div>
<h2>Lazy Loading with Intersection Observer</h2>
<LazyImage src="https://example.com/images/ny-pizza.jpg" alt="New York Pizza" />
<LazyImage src="https://example.com/images/seattle-coffee.jpg" alt="Seattle Coffee" />
</div>
);
};
export default App;I executed the above example code and added the screenshot below.

This method gives you full control and doesn’t require any third-party dependencies. However, it’s slightly more code-intensive.
Method 3 – Lazy Loading with React Suspense and Dynamic Imports
React’s built-in Suspense can also be used for lazy loading, though it’s typically for components rather than images. Still, it’s worth knowing if you want to lazy-load image-heavy components.
import React, { Suspense, lazy } from "react";
const LazyImageGallery = lazy(() => import("./ProductGallery"));
const App = () => {
return (
<div>
<h1>React Lazy Load with Suspense</h1>
<Suspense fallback={<p>Loading gallery...</p>}>
<LazyImageGallery />
</Suspense>
</div>
);
};
export default App;I executed the above example code and added the screenshot below.

This approach is great if your image gallery itself is a large component that you want to load only when needed (for example, when the user clicks a button or navigates to a specific route).
Additional Tips for Optimizing Image Loading
Here are a few extra tips that I’ve learned from building image-heavy React dashboards for U.S.-based retail clients:
- Use WebP format – It’s smaller and faster than JPEG or PNG.
- Compress images before uploading.
- Set width and height attributes to prevent layout shifts.
- Combine lazy loading with caching for maximum performance.
When I first implemented lazy loading in a React-based restaurant menu app, the load time dropped from 6 seconds to just under 2 seconds. The difference was immediately noticeable, especially on mobile networks.
Lazy loading is one of those small optimizations that make a massive difference in real-world performance.
That’s all for this tutorial. I hope you found it helpful and easy to follow. If you try this out in your own React project, you’ll notice how much smoother your app feels, especially when dealing with multiple high-resolution images.
You may also read:
- How to Use a React Time Picker Component
- How to Import SVG as a React Component
- Make a Component Draggable in React
- Use React Notifications Component

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.