Rendering Images The Good Way In Your React Application
Tags: react, webdev, frontend, ui
Published: 2024-06-17 21:36:45

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6fk4kvbm2s1q9cxjsydk.jpg)

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. <br>
_**Do:**_ `<img src="imageURL/relativePath" alt="a good image description" />` <br>
_**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" />` <br>
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4rhka0cq1dmznvzjsw7g.png)
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.<br>
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`. <br>
`<img src="imageURL/relativePath" alt="a good image description" width="200px" height="300px" />` <br>
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" />` ```css
img {
  width: 200px;
  height: 300px;
}
``` 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. ```html
<div width="200px" height="300px">
  <img src="imageURL/relativePath" alt="a good image description" width="100%" height="100%" />
</div>
``` In order for `aspect-ratio` to take effect, at least one of the image sizes must be `auto`.<br>
`<img src="imageUrl/relativePath" alt="a good image description" width="200px" height="auto" aspect-ratio="1/1" />`<br>
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. <br>

#### - <u>_The loading attribute_</u>
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"`).<br>
`<img src="imageURL/relativePath" alt="a good image description" loading="eager" />`<br>
`<img src="imageURL/relativePath" alt="a good image description" loading="lazy" />`<br>
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tv3i4bnejt3ngm9czqtv.png)

#### - _<u>React lazy-loading libraries</u>_
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. <br>
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. <br>

- _**react-lazyload:**_

**Installation:**
`pnpm install --save react-lazyload` <br>
**Basic Usage** 
```jsx
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;
```
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` <br>
**Basic Usage** 
```jsx
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;
```
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. <br>

### 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.
```html
<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" 
/>
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjint2yrcu8vvcr5fdf3.png)
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. <br>

<u>**Implementation of Progressive Image Loading with BlurHash in React**</u>

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

**Installation:**
`pnpm install --save blurhash react-blurhash`

**Basic Structure of React Blurhash component** 
```jsx
<Blurhash
  hash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
  width={350}
  height={323}
  resolutionX={32}
  resolutionY={32}
  punch={1}
/>
```
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. <br>

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1gxitry80uwxqooxk0f.png)

**Basic Usage** 
```jsx
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;
```
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. <br>
**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. 
```jsx
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;
```
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. <u>React lazy-loading libriries** 
</u>

- [_react-lazyload_](https://www.npmjs.com/package/react-lazyload) 
- [_react-lazy-load-image-component_](https://www.npmjs.com/package/react-lazy-load-image-component)

**2. <u>Example websites that uses Progressive Image Loading**
</u>

- [_unsplash.com_](https://unsplash.com/)
- [_pexels.com_](https://www.pexels.com/)

**3. <u>BlurHash</u>**

- [_React BlurHash github repo_](https://github.com/woltapp/react-blurhash)
- [_BlurHash website: blurha.sh_](https://blurha.sh/)
- [_BlurHash npm package_](https://www.npmjs.com/package/blurhash)

**4. <u>Image format converter**</u>

- [_Squoosh_](https://squoosh.app/)