Virtualizing Large Data Lists with react-window
Tags: react, tutorial
Published: 2021-09-02 17:23:23

_by author [Ikeh Akinyemi](https://blog.openreplay.com/authors/ikeh-akinyemi)_ 

Rendering large dataset in the DOM without the right tools can be expensive as it has some adverse effect on the web page performance; it slows down runtime, network requests, memory performance. In this tutorial, we’ll cover on how render large amount of data, either from an external API or a dummy data file within our application. We’ll use a React library, react-window, to render this large amount of data. Then we’ll build a simple e-commerce app displaying a list of products. 

This article will help web developers to build a better web application that displays and handles large amount of data in either a list format or grid format. It will introduce and explore solutions to the shortcomings with infinite scroll or pagination provided in **virtualization** of data. It will also cover the best UI/UX practices needed when building applications that render large data within it.

## What is virtualization?

**Virtualization** as a scrolling technique is the rendering process that keeps track of the scrolling position of a user, and visually displays only the DOM contents within that current viewport’s position. This technique simply involves handling large dataset in an infinite scroll pattern but incrementally load and render the long lists of data just as the data enters the viewport. This concept presents developers with all the performance advantages of **pagination** while at the same time providing the UX benefits gained from **infinite scrolling**.

Using virtualization is a common solution used in rendering large amount of posts in social media applications, delivering large news data in news media applications, and also in bookkeeping (Accounting) software. The endless two dimensional tile scrolling in Map applications also involves virtual scrolling which aids easy display of the constant changing locations of the users when using it. And virtualization supports displaying these large datasets in grid format or list (spreadsheets-like) format.

A simple implementation of virtualization involves a pre-calculation of the aggregate height of the list of data using the Intersection Observer API provided by the Web API and multiplying it with total count of the list items. But for this tutorial, we’ll use another easier approach, by using **react-window.**

[React-window](https://react-window.now.sh/#/examples/list/fixed-size) is a simplified version of `react-virtualized` package for easily implementing virtualization in React applications through the APIs (components) provided by the library. According to the [docs](https://github.com/bvaughn/react-window), React window works by only rendering *part* of a large dataset (just enough to fill the viewport) which reduces the amount of work (and time) required to render the initial view and to process incoming updates to the UI, and also reduces the memory footprint by avoiding over-allocation of DOM nodes.


## The Four react-window Components

Let’s go through the four available components that can be used to render lists of items when using `react-window` within React applications.


- **FixedSizeList** — This component helps render elements of fixed size defined through the `index` prop. Common available props that can be passed into this component are: `useIsScrolling`, `itemCount` , `itemSize`, `itemData`, `innerElementType`, etc


- **VariableSizeList** — This component accepts the same props as the `FixedSizeList` component, but with additional props like an `estimatedItemSize`, and `itemSize` which takes in a function in this case and returns the size of a item in the direction being windowed


- **FixedSizeGrid** — With this component, we’re rendering using dimensions: vertical (columns) and horizontal (rows) directions. Another difference is that we’ll have to add a data’s count. Common props are `rowCount`, `rowWidth`, `columnCount`, `columnWidth`, `height`, `width` e.t.c


- **VariableSizeGrid** — This component is as `FixedSizeGrid`, with additional props: `columnWidth` & `rowHeight`  which accepts functions as their prop values.

For this tutorial, we’ll focus on using the `FixedSizeList` component, which gives a beginner-friendly introduction to using `react-window` within our React applications (a simple e-commerce application displaying a list of products.).

## Implementing react-window In React Apps

Now let's install `react-window` using `yarn` in a React application. In this tutorial, `react-window` will be installed as a dependency, while the types for it will be installed as a `devDependency`. We’ll also install `faker.js` as dependency:


```
yarn add react-window faker
yarn add -D @types/react-window
```


After the installation has successfully been completed, go ahead to open the `./src/App.js` file and include the following JSX markup:


```javascript
import { useState } from "react";
import * as faker from "faker";

const App = () => {
  const [data, setData] = useState(() =>
    Array.from({ length: 1200 }, faker.commerce.product)
  );

  return (
    <main>
      <ul style={{ width: "400px", height: "700px", overflowY: "scroll" }}>
        {data.map((product, i) => (
          <li key={i + product}>{product}</li>
        ))}
      </ul>
    </main>
  );
};
```


In the above code, we defined the structural markup for the list of data being rendered into the DOM. Then passed a basic style prop to the `ul` tag to add a fixed width and height styles, also with an overflow set to `scroll` on it. Also we’ll use the React hook, `useState`, to mange our `data` state provided by `faker.js`'s dummy data. We create an array of size 1200 from it, and populate the array with enough data from `faker.js`.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fu0w9avb1s3bf1b24mvx.png)


Next, we’ll start implementing virtualization using the `react-window`’s component `FixedSizeList`. This component is responsible for rendering an individual item of a fixed size specified by the index prop within the list:


```javascript
import { useState } from "react";
import * as faker from "faker";
import { FixedSizeList as List } from "react-window";

const App = () => {
  const [data, setData] = useState(() =>
    Array.from({ length: 1200 }, faker.commerce.product)
  );

  return (
    <main>
      <List
        innerElementType="ul"
        itemData={data}
        itemCount={data.length}
        itemSize={20}
        height={700}
        width={400}
      >
        {({data, index, style }) => {
          return (
            <li style={style}>
              {data[index]}
            </li>
          );
        }}
      </List>
    </main>
  );
};
```


The `List` element uses two markup approaches defined by it’s props, the `innerElementType` and `outerElementType` props. Their default values are `divs` but in our use case, we’ll set the `outerElementType` to `ul`. Then we’ll pass in `li` as the children prop to the `List` component, providing it with a necessary styles parameters (absolute positioning styles, etc.). Also providing a width and height props to pass the previous styles we earlier defined through the `style` prop. Then using the `itemCount` prop, we are creating an imaginary array with the same length of our state, `data`. Then we explicitly make this `data` available to the `li`  elements through the `itemData` prop. 

Let’s modify the unique value for the `key` prop on the `li` elements by the using the `itemKey` prop made available on the `List` component. We’ll use the `faker.datatype.uuid` function to generate random UUID and populate the `itemKey` prop with it.


```javascript
...
const App = () => {
  ...
  return (
    <main>
      <List
        innerElementType="ul"
        itemData={data}
        itemCount={data.length}
        itemKey={faker.datatype.uuid}
        itemSize={20}
        height={700}
        width={400}
      >
        {({data, index, style }) => {
          return (
            <li style={style}>
              {data[index]}
            </li>
          );
        }}
      </List>
    </main>
  );
};
```


This code will make sure we’re using React’s lists and key best practices as defined by the [docs](https://reactjs.org/docs/lists-and-keys.html). The term “key” is used to describe a special string attribute that needs to be included when creating lists of elements in React applications.

With `react-window`, we can go further to define more structured UI elements within the children prop passed into the `List`  component without tampering with the optimised performance. 


```javascript
...
const App = () => {
 const [data, setData] = useState(() =>
    Array.from({ length: 1200 }, () => ({
      productImg: faker.image.business(200, 600, true),
      product: faker.commerce.product(),
      productPrice: faker.commerce.price(2, 22, 1, "$"),
      productDescription: faker.commerce.productAdjective(),
    }))
  );  return (
    <main>
      <List
        innerElementType="ul"
        itemData={data}
        itemCount={data.length}
        itemKey={faker.datatype.uuid}
        itemSize={20}
        height={700}
        width={400}
      >
        {({data, index, style }) => {
          return (
             <li className="py-4 border-2 border-indigo-400 flex" {...props}>
        <img className="h-10 w-10 rounded-full" src={productImg} alt="" />
        <div className="ml-3">
          <p className="space-x-1">
            <span className="text-base text-gray-900 whitespace-nowrap">
              {product}
            </span>
            <span
              className="text-gray-600 text-base font-extrabold"
              style={{
                backgroundImage:
                  "linear-gradient(transparent 50%, pink 50%, pink 85%,transparent 85%,transparent 100%)",
              }}
            >
              {productPrice}
            </span>
          </p>
          <p className="text-sm text-gray-500">{productDescription}</p>
        </div>
      </li>
          );
        }}
      </List>
    </main>
  );
};
```


Here, we updated the state of our application with more data fields to help model an online shop that has the following attributes: image (`productImg`), name (`product`), price (`productPrice`) and description (`productDescription`) of its products. We also defined more elements to hold this additional data.

(Note: To replicate the UI styles used in this tutorial, make sure to include the below tailwind.css CDN to the `/public/index.html` file.)


```html
<link
      href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
      rel="stylesheet"
    />

```


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvmzzz1he92wfqnlsw3b.png)


If you inspect the example `List` component using browser Devtools, you’ll notice that no matter how lengthy we scroll down the long list of the items within our application, the number of items in its parent div never changes. What does change is how far each one is absolutely positioned from the top. Thus, making sure we see only the UI elements for the items in the viewport.

## Open Source Session Replay
Debugging a web application in production may be challenging and time-consuming. [OpenReplay](https://github.com/openreplay/openreplay) is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue. 
It’s like having your browser’s inspector open while looking over your user’s shoulder.
OpenReplay is the only open-source alternative currently available.

![OpenReplay](https://raw.githubusercontent.com/openreplay/openreplay/main/static/replayer.png)

Happy debugging, for modern frontend teams - [Start monitoring your web app for free](https://github.com/openreplay/openreplay).


## Conclusion

In this tutorial, we’ve discussed on how to leverage virtualization to optimize the DOM when rending large amount of dataset into it. Then introduced `react-window` as a React package that efficiently implements virtaulization behind the hood, to render a large dataset and at the same time speeds up performance targets within our application. It does this by rendering only the data that are within the viewport of the browser. Get the code used in this tutorial on [GitHub](https://github.com/IkehAkinyemi/react-window).


## Resources
- [React-window Documentation](https://react-window.vercel.app/#/api/FixedSizeList)
- [Faker Documentation](https://fakerjsdocs.netlify.app/#browser-demo)
- [React Lists and Key Documentation](https://reactjs.org/docs/lists-and-keys.html)
- [GitHub link to code](https://github.com/IkehAkinyemi/react-window)