DEV Community

Cover image for How to Smoothly Render Images in React App?
Sai Krishna
Sai Krishna

Posted on • Edited on • Originally published at codebrahma.com

58 16

How to Smoothly Render Images in React App?

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

This article is cross-posted on Codebrahma and Medium

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

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

Here is the Completed Component - RenderSmoothImage.

// RenderSmoothImage.jsx
import React from "react";
import './styles.scss';
export default ({ src, alt = "notFound", objectFit = "contain" }) => {
const [imageLoaded, setImageLoaded] = React.useState(false);
const [isValidSrc, setIsValidSrc] = React.useState(!!src);
return (
<div className="smooth-image-wrapper">
{isValidSrc ? (
<img
className={`smooth-image img-${imageLoaded ? "visible" : "hidden"}`}
style={{ objectFit }}
src={src}
alt={alt}
onLoad={() => setImageLoaded(true)}
onError={() => setIsValidSrc(false)}
/>
) : (
<div className="smooth-no-image">{alt}</div>
)}
{isValidSrc && !imageLoaded && (
<div className="smooth-preloader">
<span className="loader" />
</div>
)}
</div>
);
};
// styles.scss
.fullSize {
width: 100%;
height: 100%;
}
.smooth-image-wrapper {
@extend .fullSize;
position: relative;
.smooth-image {
transition: opacity 1s;
@extend .fullSize;
}
.smooth-no-image {
display: flex;
align-items: center;
justify-content: center;
@extend .fullSize;
background-color: #fcfcfc;
text-transform: capitalize;
}
.img-visible {
opacity: 1;
}
.img-hidden {
opacity: 0;
}
@keyframes preloader-block {
from {
background-position: 0%, 0;
}
to {
background-position: 170%, 0;
}
}
.smooth-preloader {
position: absolute;
top: 0;
left: 0;
@extend .fullSize;
background: rgba(245, 245, 245, 0.34);
.loader {
background-image: linear-gradient(to right, #e6e6e6, #ffffff, #e6e6e6);
@extend .fullSize;
display: inline-block;
background-size: 200%;
animation: preloader-block 2s linear 0s infinite reverse forwards;
}
}
}
view raw styles.scss hosted with ❤ by GitHub
Final Code — RenderSmoothImage — React Component

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


Little Back Story

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)

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 poor 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 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'} />
Enter fullscreen mode Exit fullscreen mode

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>
    )
  }
Enter fullscreen mode Exit fullscreen mode
/** styles.css */

  .smooth-image {
    transition: opacity 1s; 
  }
  .image-visible {opacity: 1}
  .image-hidden {opacity: 0}
Enter fullscreen mode Exit fullscreen mode

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

Then we use 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 Gist at the top for complete styles.

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


What if Image Download Fails / Invalid Src?

Image for alternate text with image tag

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>
    )
  }
Enter fullscreen mode Exit fullscreen mode
    /** styles.css */
    ......
    .smooth-no-image {
      background-image: linear-gradient(90deg,#ccc,#999,#ccc);
      color: #fff;
    }
Enter fullscreen mode Exit fullscreen mode

There you go,

Customized Alternate Text for Image

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.

Thank you for reading this post, This is Sai Krishna Prasad, a self-taught and passionate web developer. Signing off Bubye….. until next time.

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (13)

Collapse
 
krrish96 profile image
Sai Krishna • Edited
Comment hidden by post author
Collapse
 
vaibhavkhulbe profile image
Vaibhav Khulbe

Great post. Will definitely use in one of my projects.

Collapse
 
krrish96 profile image
Sai Krishna

I am glad, it's helpful. Thank you.

Collapse
 
suvammishra9 profile image
suvammishra9

Interesting stuff. Great thinking.

Collapse
 
krrish96 profile image
Sai Krishna

Thank you.

Collapse
 
monfernape profile image
Usman Khalil

So cool.

Collapse
 
krrish96 profile image
Sai Krishna

Thank you.

Collapse
 
frempongandrews profile image
Andrews Frempong

great stuff

Collapse
 
krrish96 profile image
Sai Krishna

Thank you.

Collapse
 
fcfidel profile image
Fidel Castro

This awesome, thank you

Collapse
 
krrish96 profile image
Sai Krishna

Thank you.

Collapse
 
ahmedam55 profile image
Ahmed Mahmoud

That's such a great idea. Also using progressive images would be an elegant solution.

Collapse
 
krrish96 profile image
Sai Krishna

Yes, Thank you!

Some comments have been hidden by the post's author - find out more

nextjs tutorial video

Youtube Tutorial Series 📺

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay