How to smoothly render images in a React app

Smooth Image Rendering

Improve your web app UX by enhancing image render with React’s onLoad event and simple SCSS.

Let’s cut to the chase. The GIF below shows what we are going to achieve by the end of this post.

Default Image Rendering vs Smooth Image Rendering using render-smooth-image-react

Here is the completed component gist - RenderSmoothImage.

I have published this as an npm package render-smooth-image-react. The source code is available on GitHub.


A little backstory

I recently started working on a media-heavy app. Everything was cool until I noticed a bunch of images rendering poorly, which looked something like this.

Example: Poor image rendering (printing)

For a moment I thought, “Did I end up building a 🖨 printer simulator app?”

Overall customer satisfaction will take a hit with such a poorly loading UX (even if the rest of the app is great). This is especially true for media-heavy apps.

Alright, Let’s see what we can do to fix this.


Load and fire 🔫

Let the browser download the image and then render it.

The perfect moment to render an image is after downloading completely. Till then, we just show a loader/placeholder and hide the image.

We can achieve this by using React’s onLoad event on the image tag. You can read more about React.js events here.

<img onLoad={'callbackAfterImageIsDownloaded'} />

Code it

We will create a functional component and use hooks to maintain a couple of states. If you are new to React hooks, you can learn more here.

  // RenderSmoothImage.jsx

  function RenderSmoothImage({src, alt}) {
    const [imageLoaded, setImageLoaded]=React.useState(false);

    return (
      <div className="smooth-image-wrapper">
        <img
          src={src}
          alt={alt}
          className={`smooth-image image-${
            imageLoaded ? 'visible' :  'hidden'
          }`}
          onLoad={()=> setImageLoaded(true)}}
        />
        {!imageLoaded && (
          <div className="smooth-preloader">
            <span className="loader" />
          </div>
        )}
      </div>
    )
  }
/** styles.css */

.smooth-image {
  transition: opacity 1s;
}
.image-visible {
  opacity: 1;
}
.image-hidden {
  opacity: 0;
}

Here, we maintain a state, imageLoaded, that defaults to false. It will be set to true once the image is downloaded. We use the onLoad event to trigger this.

Then, we use the imageLoaded state to conditionally add classes to the img tag, image-visible vs image-hidden. We can add transitions/animations to make it smoother. Please refer to the linked list at the top for complete styles.

That’s it! No more printer simulators on the page.


What if the image download fails/Invalid src?

Image for alternate text with image tag

By using the alt attribute, we can show alternate text for the image. However, the default icon and styling isn’t too great.

To fix this we can display custom alt text.

  // RenderSmoothImage.jsx

  function RenderSmoothImage({src, alt}) {
    .....
    const [isValidSrc, setIsValidSrc] = React.useState(!!src);

    return (
      <div className="smooth-image-wrapper">
        {isValidSrc ? (
          <img
            ....
            onError={() => setIsValidSrc(false)}
          />
        ) : (
          <div className="smooth-no-image">{alt}</div>
        )}
        {isValidSrc && !imageLoaded && (
          <div className="smooth-preloader">
            <span className="loader" />
          </div>
        )}
      </div>
    )
  }
/** styles.css */
......
.smooth-no-image {
  background-image: linear-gradient(90deg, #ccc, #999, #ccc);
  color: #fff;
}

There you go,

Customized Alternate Text for Image

You can style the alternative text any way you want.

I made things easy for you and published a light-weight npm package render-smooth-image-react.

Attributions:
  1. undefined by undefined