DEV Community

Cover image for Rendering Images The Good Way In Your React Application
Stanley Azi
Stanley Azi

Posted on • Edited on

Rendering Images The Good Way In Your React Application

Introduction

Most times on my initial visit to a website, I take my time to observe and enjoy how images are rendered. In fact, if the website happens to include an image on their hero section, that is what first catches my attention before I go ahead to read the texts on the page.

Visual appealing images aids good User Experience (UX) while browsing a web or mobile application. It also play a crucial role in beautifying the User Interface (UI). In some cases, images speaks louder than the texts associated with it.

Rendering images poorly in your application could harm your UI, thereby causing bad UX. In this article, I will walk you through "rendering images the good way in your react application".

Prerequisites

This article is for everyone who loves building and beautifying web and mobile applications, and those who use them. However, having basic understanding of the below tools and concepts in web development is a plus as we would write some code to better explain some concepts.

  • Basic knowledge of Frontend Development (HTML/CSS/JS)
  • Basic Knowledge of React

What does Image Rendering mean?

Image Rendering is the process of displaying images on the UI of an application for the users to see and interact with them. When you visit an application and an image gets loaded on the screen, that process of the image loading is what is referred to as Image Rendering.

Why should we care about how images are rendered?

There are more to Image Rendering than just loading images on the User Interface (UI). If images are not properly rendered, they tend to make a bad UI and even harm User Experience (UX). This is why proper image rendering is considered a critical skill every Frontend Developer should possess.

How to render images the good way in a react app?

The basic rendering of images in most Frontend Web Development languages, libraries and frameworks follow a simply convention where the HTML img element is used. The img tag primarily consists of two major standard attributes; src and alt attributes. The src attribute takes a string of the url or the relative path of the image to be rendered, while the alt attribute takes a string containing a description of the image to be rendered. The description provided in the alt attribute is what would be rendered if the image is broken or failed to render. The alt attribute also aids accessibility (a11y) by describing to screen readers what the image is. Other img tag standard attributes include srcset, sizes, loading, crossorigin, usemap, etc.

HTML img tag attributes' meaning breakdown

React renders HTML elements using JSX. The JSX translates to HTML under the hood.

In order to render images the good way in a react application, below are the tips and tricks that would magnify how they are rendered by react:

1. Emphases on the alt attribute

The alt attribute is very important and should never be omitted when rendering images with the img tag. The alt attribute does not only display the provided description when the image failed to load, but also play a crucial role in aiding a11y by providing screen readers contents to read out to the user about the image. I have once debugged someone's code and discovered that all img tags in the entire code base does not have alt attribute, I had to include it and provide good description for each each image. Not including the alt attribute is a bad practice and should be avoided at all times.

Do: <img src="imageURL/relativePath" alt="a good image description" />

Don't: <img src="imageURL/relativePath" />

2. Best image format and file size

Generally, the image file format and size matters a lot. They determine the clarity and the speed at which the image is loaded.

The smaller the image size, the faster it would render.

It is highly recommended that the file format for icons and small graphics should always be SVG as they are usually smaller in size and scalable.
<img src="iconUrl.svg" alt="a good icon description" />

The WebP format is one of the best formats to save and render images of high quality and size. It is a modern format that provides great compression and high quality when compared to old formats like JPEG and PNG.
<img src="imageUrl.webp" alt="a good image description" />
squoosh.app image format converter
The image above shows an image format conversion from JPG to WebP using Squoosh.app. In this conversion, the 2.79MB JPG image (see bottom-left corner of the image) was converted to a 705kB WebP format, saving 75% of the file size while still maintaining the image quality (see bottom-right). You can further adjust the quality and compare the difference by dragging the divider left-right to get smaller size. Squoosh.app is a great tool for image conversion that does not upload your image elsewhere as you are basically doing all your conversion offline and with your computer, and it has a download button that you can click to simply save your file after conversion. The Squoosh.app also converts to other image formats which you can explore.

Other image formats (e.g, JPEG and PNG) are still valid and can be used if the image size is small, but should be used with caution. Since WebP saves us a lot image size while still maintaining high quality, why not just use it all the time in your application? Its support across browsers as at the date of the publish of this article is 96.9%, which means it has good support across all major browsers.

3. Proper image box sizing

Providing a proper size for the image to be rendered should be practiced. This backs the certainty of the image fitting the available space for it across screens as desired. The intrinsic sizing being discussed in this case include width, height and aspect-ratio.

<img src="imageURL/relativePath" alt="a good image description" width="200px" height="300px" />

The image sizing, just like every other HTML element, could be styled inline, internally or with an external css file.
<img src="imageURL/relativePath" alt="a good image description" />

img {
  width: 200px;
  height: 300px;
}
Enter fullscreen mode Exit fullscreen mode


If the image is wrapped with a container, e.g div or span, and the desire is to make it cover the container, in most cases the container would take the size of the img tag. But if the container has a specified width and height, the img tag could be given a relative value (e.g width="100%"), although this should be applied with careful examination as the aspect-ratio of the image may affect the styling and should be adjusted accordingly.

<div width="200px" height="300px">
  <img src="imageURL/relativePath" alt="a good image description" width="100%" height="100%" />
</div>
Enter fullscreen mode Exit fullscreen mode


In order for aspect-ratio to take effect, at least one of the image sizes must be auto.

<img src="imageUrl/relativePath" alt="a good image description" width="200px" height="auto" aspect-ratio="1/1" />

Sizing the image is one of the basic good practice that aids good User Interface (UI). When an image is properly sized, you are certain as a Frontend Developer that its shape across screens gives a pleasing view to the user.

4. Lazy loading

Diving deeper into "rendering images the good way", one of the critical aspect to be considered is how the image is rendered. One of the key ways to render an image wholly is to lazy-load it. This ensures that the image is in the viewport and has fully been loaded before displaying it on the UI. To achieve this, you can use the img tag loading attribute where you set the value to "lazy", or you can use other small-size React image lazy-loading libraries that may include extra loading features.

- The loading attribute

The loading attribute gives us the ability to control how the image is rendered. It helps to command the image to either load immediately the UI is loaded regardless of whether the image is in the viewport (loading="eager"), or load only when the image reaches a certain viewport (loading="lazy"). This means that the loading attribute can take values, eager and lazy. By default, images are rendered eagerly (loading="eager").

<img src="imageURL/relativePath" alt="a good image description" loading="eager" />

<img src="imageURL/relativePath" alt="a good image description" loading="lazy" />

Lazy-loading images generally improves image rendering. It ensures that the image only loads when needed, and in most cases, it forces the image to only display when it is fully ready, thereby preventing the image from rendering part-by-part on slow or poor network.
img tag loading attribute

- React lazy-loading libraries

Using a third-party library to lazy-load images is also an option. One may prefer to use a library which simply involves installing and importing the library, and then using it to control the img tag in your application as the library's documentation suggests. Although in most cases, using the loading attribute works just fine rather than importing a third-party library if you are rendering a few simple images with less configurations. Using a third-party library was preferred due to its extra features and poor browser support for the loading attribute in the past, but as at the time of the publish of this article, the img tag loading attribute is 95.73% supported across all major browsers, which means it's safe to simple use the loading attribute to lazy-load images.

Further more on the third-party library option, there are two React lazy-loading libraries I recommend for use; they are react-lazyload and react-lazy-load-image-component. Both libraries supports lazy-loading images and components.

  • react-lazyload:

Installation:
pnpm install --save react-lazyload

Basic Usage

import LazyLoad from 'react-lazyload';

function App() {
  return (
    <>
     <h1>Lazy loaded image</h1>

     <LazyLoad height="100%" placeholder={ <p>Loading...</p> }>
      <img 
        src="imageURL/relativePath" 
        alt="a good image description" 
        width="200px"
        height="300px"
      />
     </LazyLoad> 
    </>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The react-lazyload library wraps the img tag with it's LazyLoad component just as shown in the code block above. It can only wrap one child img tag at a time. This library performs well with speed at which it renders images. Its simplicity and features makes it unique. Some of the props the LazyLoad component can take include placeholder, once, height, unmountIfInvisible, debounce/throttle, overflow, etc.

  • react-lazy-load-image-component library:

Installation:
pnpm i --save react-lazy-load-image-component

Basic Usage

import { LazyLoadImage } from 'react-lazy-load-image-component';

function App() {
  return (
    <>
     <h1>Lazy loaded image</h1>

     <LazyLoadImage
        src="imageURL/relativePath"
        placeholder={ <p>Loading...</p> }
        alt="a good image description"
        height="400px"
        width="600px" 
      />   
    </>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The features of this library makes it easier to handle the loading state of the image. Unlike react-lazyload library which wraps the img tag with an opening and closing tag (<LazyLoad><img src=β€œβ€ alt=β€œβ€ /></LazyLoad>), this library makes it seem as though you are still using the img tag, you simply replace the img with LazyLoadImage and all the attributes of img tag can be passed to the LazyLoadImage component as props. React-lazy-load-image-component library performs great when the images to be rendered are many, it ensures that only images that would fit the viewport are fetched, the more you scroll, the more it fetches, saving the amount of network requests that would have been made if all images were to be fetched at once. The props that could be passed to this component aside all img tag attributes include placeholderSrc, threshold, effect, useIntersectionObserver, debounce/throttle, onLoad, beforeLoad, afterLoad, placeholder, etc.

5. Responsive Images

Generally, responsiveness is a very important aspect of Frontend Development that ensures that all UI components and elements of an application are displayed in correct sizes across devices. The concept of displaying the correct size of an element with respect to the device's screen size can be applied to the img tag with the srcset attribute for image rendering, where different sizes are passed to the sizes attribute with respect to the number of image URL passed to the srcset attribute.

<img 
  src="imageURL-small" 
  srcSet="imageURL-small 500w, imageURL-medium 1000w, imageURL-large 1500w"
  sizes="(max-width: 600px) 480px, (max-width: 1200px) 800px, 1200px"
  alt="a good image description" 
/>
Enter fullscreen mode Exit fullscreen mode

The above code block shows a basic application of responsive image where different image URLs were provided in the srcset attribute, and their respective sizes were provided and separated with commas (,) in the sizes attribute. This ensures that the correct size of the device screen size is loaded, which improves rendering speed with respect to the image file size. A default URL is passed to the src attribute.

6. Progressive Image Loading

The concept of progressive image loading involves rendering a low-quality image placeholders (LQIP) or a blurred version of the original image to be displayed while it is loading. A typical example of this type of image rendering is seem on unsplash.com and pexels.com.
progressive image loading
I personally recommend rending images progressively as it gives your users/visitors a picture of what is expected to be loaded. This improves both UI and UX because it initially occupy a space for the loading image which prevents Cumulative Layout Shift (CLS), and also provide the user a hint of what is loading.

Implementation of Progressive Image Loading with BlurHash in React

BlurHash is a compact representation of a placeholder for an image.

Installation:
pnpm install --save blurhash react-blurhash

Basic Structure of React Blurhash component

<Blurhash
  hash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
  width={350}
  height={323}
  resolutionX={32}
  resolutionY={32}
  punch={1}
/>
Enter fullscreen mode Exit fullscreen mode

The React Blurhash components takes hash, width, height, resolutionX, resolutionY, and punch as props.
The hash prop's value is a string of the encoded hash of the original image to be rendered. The width and height props can be a valid CSS string or number value for width and height of the Blurhash image. The resolutionX, resolutionY controls the horizontal axis and vertical axis resolution of the blurred image, while the punch takes care of contrast of the blurred image.

To generate a BlurHash string, we would simply use the generator on the BlurHash website (blurha.sh) for this article. On blurha.sh, scroll down to find the image upload button, upload the image you need the BlurHash string for and copy the generated hash.

blurhash website

Basic Usage

import { useState, useEffect } from "react";
import { Blurhash } from "react-blurhash";

const [imageLoaded, setImageLoaded] = useState(false);
const [imageError, setImageError] = useState(false);
const [imageSrc, setImageSrc] = useState(null);

useEffect(() => {
  const img = new Image();
  img.onload = () => {
    setImageSrc(img.src);
    setImageLoaded(true);
  };
  img.onerror = () => {
    setImageError(true);
  };
  img.src = "imageUrl/relativePath";
}, []);

function App() {
  return (
    <div>
      {!imageLoaded && !imageError && (
         <div>
           <Blurhash
             hash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
             width="100%"
             height="100%"
             resolutionX={32}
             resolutionY={32}
             punch={1}
           />
         </div>
      )}
      {imageError && <div>Error loading image</div>}
      {imageSrc && <img src={imageSrc} alt="a good image description" loading="lazy" />}
    </div>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In the above use case of the React BlurHash component, I manually handled the loading state and error state of the image without using a React library. The code simply uses useEffect() to watch updates of the state of the rendering image. When the loading is true, the BlurHash component is shown, but when the image is fully loaded, the original image is shown to replace the the BlurHash component, and on error, the error state becomes true and an error message is displayed instead (in our case 'Error loading image').

You may be asking why all these lines of code just to display an image?. The desire to better handle image rendering in React got us here. Although we can use a React library to save our time and reduce the lines of code.

Using react-lazy-load-image-component library with BlurHash
The react-lazy-load-image-component performs well with BlurHash when you simply pass the Bluhash component to its placeholder prop.

import { LazyLoadImage } from 'react-lazy-load-image-component';
import { Blurhash } from 'react-blurhash'

function App() {
  return (
    <>
      <h1>Lazy loaded image</h1>
      <LazyLoadImage
          src="imageUrl/relativePath"
          placeholder={
            <Blurhash 
              hash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
              height={400}
              width={600}
              resolutionX={32}
              resolutionY={32}
              punch={1}
            />
          }
          alt="a good image description"
          height="400px"
          width="600px"
        />   
    </>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The above code block greatly reduced lines of code compared to when the image state was controlled manually. You can also decide to observe the loading state of the LazyLoadImage component with the onLoad prop and update the state when the image is loaded (e.g, const [loading, setLoading] = useState(true)) rather than using the placeholder prop. The onLoad prop can be assigned a function that updates the loading state (e.g, () => setLoading(false)), which then gives the ability to conditionally display the Blurhash component based on the loading state of the image. Although the option of using the onLoad prop to manually manage loading state can cause Cumulative Layout Shift (CLS) if the wrapper div or span containing the Blurhash component and the LazyLoadImage component is not properly styled.

7. Other Ways Of Rendering Images Efficiently

There are lots of good ways of rendering images. This means we have options as developers. These options include, but are not limited to Preloading Key Images, Using Image CDN, Using Background Images for Decorative Images, and Exploring Other Methods Of Progressive Image Rendering.

Conclusion

Effectively rendering images in your application is crucial for optimizing performance and greatly improving User Experience (UX). Also, this great act makes your User Interface (UI) more charming and lovely to visit regularly.

"When images are properly rendered on the UI of your application, kindly note that the joy I have in heart as your dearest user and frequent visitor is beyond imagination".

Always enable code analysis tools like ESLint in your coding environment to alert you when you make mistakes like forgetting to include the almighty important alt attribute in your img tag.

As a Frontend Developer or a UI Engineer, make it a priority to render images following best practices that would greatly improve how fast they are rendered by firstly considering the sizes and formats of your images before using them in your application.

Thank you for reading through to this part of the article!
Let's keep following best practices while coding! πŸš€

Resources

1. React lazy-loading libriries

2. Example websites that uses Progressive Image Loading

3. BlurHash

4. Image format converter

Top comments (14)

Collapse
 
faave profile image
Favour Ukonu

πŸ”₯ Saving this because I'll reference this when implementing lazy loading for images. This is good!πŸ’ͺ

Collapse
 
stan015 profile image
Stanley Azi

Yes! Thank you, Favour. I’m glad it made sense.
Let’s keep coding.πŸš€

Collapse
 
chidinma_nwosu profile image
Chidinma Nwosu

I will definitely reference this article when working on anything that requires images or image rendering, this is a really good read.

Collapse
 
stan015 profile image
Stanley Azi

Sure, you should. πŸ‘
I am happy it was worth reading. πŸš€
Thank you, Chidinma.

Collapse
 
solomon_7 profile image
Solomon Uzoma

Thank you for this man, you did great here πŸ‘

Collapse
 
stan015 profile image
Stanley Azi

I’m glad it’s resourceful! 🀝
Let’s keep coding. πŸš€

Collapse
 
blaycoder profile image
blaycoder

Insightful πŸ”₯πŸ’―

Collapse
 
stan015 profile image
Stanley Azi

I'm glad it is! πŸ™Œ πŸš€

Collapse
 
oluwasetemi profile image
Ojo Oluwasetemi

Awesome πŸ‘Œ. You can check the next/Image component or the GatsbyImplementation of Image component.

Collapse
 
stan015 profile image
Stanley Azi

Thank you! πŸ™Œ. I have seen you play around Next/Image component in one of your NextJS YouTube livestreams. I have also played around with it and it's truly mind-blowing. I would check out the Gatsby Implementation as well.

I was focused on pure React in the article, but I should have included React Frameworks' implementation somewhere in the article because they are truly worthy mentions!

I appreciate your insight once again! πŸš€

Collapse
 
rolalove profile image
kofoworola shonuyi

Oh my πŸ˜Œβ€¦.definitely putting this to practice!!

Collapse
 
stan015 profile image
Stanley Azi

Yes! πŸš€

Collapse
 
iz_prince profile image
Prince Vick

My bossπŸ™Œ you dey always deliver.

Collapse
 
stan015 profile image
Stanley Azi

Thanks brotherly πŸ™Œ
I’m glad I did here πŸš€
Let’s keep coding!