Lazy Loading Images in React for Better Performance
Tags: react, javascript
Published: 2020-06-16 16:49:16

In some cases, we need to load a list that contains a lot of images on a single page. Most of the images not even visible on the screen when it is loaded. We need to scroll down to see them. By default, when we open the page, the browser would load and request all the images on the page regardless if it’s visible on the viewport or not. That’s perfectly fine in some conditions, but when we have a lot of images that are not visible on the viewport and load all of them at the same time, that may lead to performance issues.

In that condition, it’s better to only load the images that appear on the viewport. It improves the performance cause the browser will postpone loading the images below the viewport until the user scrolls the page and reach the images.

## Let’s Start
There are several ways to do lazy loading including a native way, but when I write this post, native lazy loading is only supported in Chromium-based browsers and Firefox. So for wider browsers support, we’re going to do lazy loading using react-lazyload, and styled-components for styling. You can start by installing these packages:

```
npm install --save react-lazyload styled-components

// or if you prefer yarn:

yarn add react-lazyload styled-components
```

## Create LazyImage component
We’ll use this component when we want to lazy-load images.

{% gist https://gist.github.com/naufaal98/a20b1e55393bd7316c7e2b5654295aed %}

The LazyImage component contains `ImageWrapper`, `Placeholder`, `LazyLoad`, and `StyledImage`. Anything inside LazyLoad would not load until it appears on the viewport, that’s why we put `StyledImage` inside it.

`Placeholder` is just an empty `div` with animation to indicate the image is still loading. When the image finally loaded then we call `removePlaceholder` to remove `Placeholder` from the DOM. I use `refs` to do that instead of updating the state to prevent unnecessary re-rendering. If you’re unfamiliar with refs in React, you can learn more about it [here][1].

You can create shimmer or put spinner inside the `Placeholder`, but I just made it simple here with animated background. Set the `Placeholder` size the same as the image size so the transition will be smoother. In the example above, the size of `Placeholder` and `StyledImage` will follow the `ImageWrapper` size.

You might wanna use react-lazyload placeholder prop to put Placeholder like this:

```
<LazyLoad placeholder={<Placeholder />}>
  ...
</LazyLoad>
```

But when I tried that, the Placeholder would instantly disappear when it reaches the viewport as I scroll the page even though the image is still not fully loaded, hence I put it outside and manage it with onLoad and onError events.

## Let’s Try It
In App.js, we’ll use dummy images from Picsum Photos to demonstrate and see how the LazyImage works.

{% gist https://gist.github.com/naufaal98/39e1cf697d1e2c1c37a665ded5b64d9b %}

It’s not recommended and considered as [anti-pattern][2] to use indexes for keys. I did that in this example just to make it simple and straightforward to focus on lazy loading.

When we run the App, it would look like this:

![The Result](https://dev-to-uploads.s3.amazonaws.com/i/pxk19e3it5awuxgtiwe3.gif)

As you can see from the gif above, images not loaded until it appears on the viewport, which improves the performance and reduces bandwidth consumption. This method also works fine in Server Side Rendering.

You can still improve it by adding different rendered elements/style when an error happens or create fade-in animation when the image appears, I leave that for you to try.

I put the code on Codesandbox, you can try and play with it [here][3].

I hope this post helps you! Thanks for reading!

[1]: https://reactjs.org/docs/refs-and-the-dom.html
[2]: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
[3]: https://codesandbox.io/s/lazyloading-image-wfei7?file=/src/App.js