Rendering long lists using virtualization with React

React

When working on web applications, we often display lists of data. If the data is not particularly lengthy, we are probably okay with rendering all elements at once. However, as our data grows, we might notice performance issues. In this article, we create a simple React application that aims to render thousands of images using a virtualization technique.

Determining the issue

First, let’s create an application that fetches a list of photos and displays them. To do that, we will use JSONPlaceholder, which exposes an endpoint that returns five thousand entries.

For starters, we create a hook that fetches the data.

usePhotos.tsx

Above, we use to imit the number of photos to a thousand for now. Trying to render more is very likely to crash our browser.

Each one of our photos has a set of simple properties.

Photo.tsx

Our list is straightforward and renders one component per photo.

PhotosList.tsx

For each photo, we render the thumbnail and the title.

PhotoCard.tsx

Measuring the performance

While the above solution works, it is far from optimal. Rendering thousands of images at once causes the browser to make thousands of HTTP GET requests to fetch all of the data. Therefore, it is going to cause issues, especially on mobile.

Also, rendering thousands of elements in the DOM tree can slow down the initial rendering of our website and cause the scrolling to stutter.

Let’s run a Lighthouse audit to measure the performance of our application.

If you want to know more about Lighhouse, check out Improving our performance, accessibility, and SEO with Lighthouse audits

Unfortunately, the above score is not very good. So, let’s try improving it.

Introducing virtualization

If we render a large list, the user does not see all its contents at once and uses a scrollbar. When we implement virtualization, we don’t render the elements of the list that are not currently visible. By doing that, we make the DOM tree creation a lot faster. Besides that, the browser does not need to fetch all the images simultaneously.

To implement virtualization in this article, we use the react-window library.

The react-window library is a lighter, more recent alternative for the react-virtualized package created by the same author.

Since every element of our list is the same size, we can use the component provided by the react-window library.

PhotosList.tsx

FixedSizeList expects us to provide it with a component that renders an element of the list using the current index.

The component used for rendering a row might be a good canditate for memoization.

This component also gets the prop used for positioning, so let’s modify our PhotoCard component to accommodate that.

PhotoCardtsx

Thanks to the react-window library, we no longer render all elements at once.

The above, combined with the fact that the browser no longer fetches all of the images, immediately improves our performance significantly.

Sizing the list automatically

So far, we’ve had to provide the width and height of the list explicitly. If we want the list to be sized automatically, we need the react-virtualized-auto-sizer library.

The react-virtualized-auto-sizer library does not support React 18, currently.

To let the list grow to fill all available space, we need to use the component.

PhotosList.tsx

We can use to manage only width or height instead of both. To do that, we need to use the or   attributes.

A crucial thing to notice is that the expands to fill the parent, but it does not stretch it. In our case, to deal with it in the simplest way possible, we can enlarge the div.

Implementing pagination

So far, we’ve fetched all data in one big request. However, to improve the performance more and handle the data of any length, we can implement infinite scrolling. The idea is to fetch additional data every time the user scrolls to the end of the list.

To do that, we need the react-window-infinite-loader package.

For the library to work, we need to use the component.

PhotosList.tsx

The needs three properties:

  • is a function that returns true if an element with a given index is loaded,
  • is a function that fetches the data as soon as the user scrolls down,
  • is a number of rows in the list that can be an arbitrarily high number if the exact number of rows is unknown.

Let’s modify our hook to accommodate for the properties the needs.

Thanks to the above changes, our application loads batches of elements as the user scrolls the list.

Summary

In this article, we’ve tackled the issue of rendering huge lists in React. To do that, we’ve used the react-window library. We’ve also learned how to integrate it with react-virtualized-auto-sizer and react-window-infinite-loader. Thanks to that, we’ve achieved an infinitely-loading list and improved the performance measured with the Lighthouse audit.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments