Ibadehin Mojeed I'm an advocate of project-based learning. I also write technical content around web development.

Progressive image loading in React: Tutorial

4 min read 1372

Progressive Image Loading React Tutorial

Images can have a great impact on websites. Their presence improves the user experience and helps increase engagement. However, loading a high-quality image can take time and make this experience more frustrating, especially on a slow internet connection.

To address this problem, developers need to deploy a strategy that supports a positive loading experience. One such strategy is progressive image loading.

In this tutorial, we will learn about progressive image loading, how to deploy this strategy in React, and more.

Why progressive image loading is useful

With progressive image loading, the developer can display a low-resolution or preview image until the actual image loads. This improves the user experience by providing awareness that images are coming up momentarily.

The GIF below demonstrates the use of a native <img /> element to render an image.

Native Img Element Loading On Webpage

As we can see, although the page is already loaded, the image takes an extra second before it’s rendered, thereby causing empty spaces. The experience worsens when we have a very slow internet connection.

By using the progressive loading technique, we can render a tiny file size version of the image to decrease load time. Once the high-resolution version is loaded, we can swap the image files. See the GIF demonstration below:

Clicking Reload Button At Top Left Of Screen To Show Result Of Progressive Image Loading With Onload Event Handler

Since the placeholder image loads almost immediately, this strategy can also help reduce the issue of layout shifts caused by webpage images. Be aware that layout shifts occur mainly because the browser is unaware of how much space to reserve for the image.



We can prevent this behavior by adding width and height attributes to the image. This notifies the browser to reserve a set amount of space for the image to occupy. We must then apply a max-width: 100% and a height: auto on the image in our CSS file to ensure the image behaves correctly in a responsive layout.

In this tutorial, we will learn how to improve the user experience and prevent layout shifts by loading images progressively in React from scratch. We will also learn how to use an external library to achieve the same result.

To follow this tutorial, you must have a working knowledge of React.

Progressive image loading techniques in React

The magic of progressive images is achieved by creating two image versions: the actual image, and a smaller file version, usually less than 2kB in size.

The low-quality image is loaded at first for quick display and scaled up to fit the main image width while the main image downloads. Then, a blur filter and proper CSS transitioning are applied.

React frameworks like Gatsby and Next.js also use this pattern in their image components. But instead of the users manually creating a small version of the image, the frameworks automatically generate it from the source image.

In addition, these frameworks use advanced image processing options and enable the lazy loading of images further down the screen.


More great articles from LogRocket:


In our case, the focus is progressive image loading in React. Let’s get started with the implementation.

Creating an image component

We will create an image component called ProgressiveImg to encapsulate an <img /> element and the logic for the progressive loading. This component can then be used as a replacement for the native <img /> elements.

const ProgressiveImg = ({ placeholderSrc, src, ...props }) => {
  return (
    <img
      {...{ src: placeholderSrc, ...props }}
      alt={props.alt || ""}
      className="image"
    />
  );
};
export default ProgressiveImg;

In the above code, the component receives the actual image source, its placeholder source, and every other prop that we pass. Then, we assigned these props to the <img /> element attributes.

Notice how we used the spread operator to inject any other props that the component receives. For instance, the component will receive a required image width and height, as we will see in a moment. In the meantime, we assigned a placeholder image source to the src for quick display, as we mentioned earlier.

Next, let’s ensure we pass all the above-mentioned props to the <ProgressiveImg /> like so:

import ProgressiveImg from "./components/ProgressiveImg";
import image from "./images/large_.jpg";
import placeholderSrc from "./images/tiny_.jpg";

export default function App() {
  return (
    // ...
    <ProgressiveImg
      src={image}
      placeholderSrc={placeholderSrc}
      width="700"
      height="465"
    />
    // ...
  );
}

As seen in the code, we passed the image and its small-size version that we resized to be less than 2kB. We must also pass along the image width and height to prevent layout shifts.

Be sure you maintain the aspect ratio if the image size is bigger than the assigned value. With this, the frontend should look like so:

Demonstrating How A Small File Size Image Appears In The Frontend

Updating the thumbnail to the actual image

To update the src of the img and render the actual image, we must store the default image source in a state variable via the useState Hook. Then, we can update the variable inside a useEffect Hook once the actual image loads.

Let’s update the ProgressiveImg component so we have the following:

import { useState, useEffect } from "react";

const ProgressiveImg = ({ placeholderSrc, src, ...props }) => {
  const [imgSrc, setImgSrc] = useState(placeholderSrc || src);

  useEffect(() => {
    // update the image
  }, []);

  return (
    <img
      {...{ src: imgSrc, ...props }}
      alt={props.alt || ""}
      className="image"
    />
  );
};
export default ProgressiveImg;

Notice that the src attribute for the img is now assigned a value of the state variable. By default, this value is set to the placeholder source if we have it. Otherwise, it is assigned the main image.

Next, let’s update the useEffect Hook so we have the following:

useEffect(() => {
  const img = new Image();
  img.src = src;
  img.onload = () => {
    setImgSrc(src);
  };
}, [src]);

In this Hook, we started by creating an img element by instantiating an Image() object and setting the src attribute to the actual image source.

With the onload event handler on the image object, we can detect when the actual image has completely loaded in the background. Then, we update the image src to the actual image.

See the result below:

Clicking Reload Button At Top Left Of Screen To Show Result Of Progressive Image Loading With Onload Event Handler

Implementing a transition blur

Let’s add a CSS transition blur for a smooth effect. In the ProgressiveImg component, add the following code above the return statement:

const customClass =
    placeholderSrc && imgSrc === placeholderSrc ? "loading" : "loaded";

We will dynamically add class names to the image based on the loading status.

So, update the <img /> to include the custom class name:

return (
  <img
    // ...
    className={`image ${customClass}`}
  />
);

If the actual image is still loading, we add a loading class to the image. Otherwise, we add a loaded class. We can then update the CSS to include the style rules:

.loading {
  filter: blur(10px);
  clip-path: inset(0);
}
.loaded {
  filter: blur(0px);
  transition: filter 0.5s linear;
}

Save and see the changes in the frontend. See the complete code on CodeSandbox.

Progressive image loading using a library

We can also load images progressively by using a library called react-progressive-graceful-image. To use it, we must install it:

npm i react-progressive-graceful-image

We can then import the ProgressiveImage component and implement it like so:

import ProgressiveImage from "react-progressive-graceful-image";
import image from "./images/large_.jpg";
import placeholderSrc from "./images/tiny.jpg";

export default function App() {
  return (
    // ...
    <ProgressiveImage src={image} placeholder={placeholderSrc}>
      {(src, loading) => (
        <img
          className={`image${loading ? " loading" : " loaded"}`}
          src={src}
          alt="sea beach"
          width="700"
          height="465"
        />
      )}
    </ProgressiveImage>
    // ...
  );
}

The ProgressiveImage component uses a render props technique to implement progressive image loading. In its children function prop, we have access to the src and loading arguments in the render callback.

With the loading argument, we can dynamically add classes to the img element. The loading returns true while the actual image is loading; otherwise, it returns false.

We have also added style rules for the classes in the CSS file. See the complete code and demo on CodeSandbox.

Conclusion

By implementing a progressive image loading technique, we can greatly improve the user experience in our React project.

In this tutorial, we covered how to load images progressively in React with and without an external library. I hope you’ve learned a lot and enjoyed reading this guide. If you have questions or contributions, I’m in the comment section.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Ibadehin Mojeed I'm an advocate of project-based learning. I also write technical content around web development.

One Reply to “Progressive image loading in React: Tutorial”

Leave a Reply