Lazy Loading Images in React

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-component

or, if you’re using Yarn:

yarn add react-lazy-load-image-component

Step 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.

Lazy Loading Images in React

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.

Lazy Loading Images React

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.

React Lazy Loading Images

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:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.