Samaila Bala I'm a frontend engineer and technical writer.

4 ways to render large lists in React

5 min read 1579

Four Ways to Render Large Lists in React

Lists are an integral part of most web applications because they help display data in a more presentable format. But when an app tries to handle too much data in a list, it often leads to performance problems.

In this guide, we’ll outline some problems associated with bloated lists and walk through steps to overcome these performance challenges in React applications.

Prerequisites

To follow along with this tutorial, you’ll need the following.

  • A general understanding of JavaScript and React
  • npm greater than v5.2 or yarn
  • Node version 12 or greater

Problem

Let’s create a sample application to demonstrate what happens to your app’s performance and the DOM tree when you try to render a large list of 10,000 records.

Launch your terminal and paste the code below to create a React application.

npx create-react-app render-list

Run the code below to install the Faker library, which we’ll use to generate random data to use in our application.

npm i faker

Next, go to the App component in the src directory and enter the code below.

import React from 'react';
import faker from 'faker'
import './App.css';

const data = new Array(10000).fill().map((value, index) => ({ id: index, title: faker.lorem.words(5), body: faker.lorem.sentences(4) }))

function App() {
  return (
    <div>
      {data.map(((item) => (
        <div key={item.id} className="post">
          <h3>{item.title} - {item.id}</h3>
          <p>{item.body}</p>
        </div>
      )))}
    </div>
  );
}
export default App;

Go the App.css file and add the lines of code below to add a little style to the list.

.post{
  background-color: #eee;
  margin: 2rem;
  padding: 1rem;
}
.pagination{
  margin: 1rem auto;
  list-style: none;
  display: flex;
  justify-content: space-evenly;
  width: 50%;
}
.active{
  border: 1px solid black;
  border-radius: 100%;
  padding: 0 3px;
  outline: none;
}

The above code renders a list of 10,000 records.

Start the React application in your browser and open your console.

We made a custom demo for .
No really. Click here to check it out.

Example Rendered List

When the page loads, you’ll notice it takes time to render the list. There is also a noticeable lag when you scroll.

Below are four ways to solve list-related performance issues in React apps.

1. Pagination

Pagination allows you to render data in pages as opposed to rendering all the information at once. This way, you basically control the amount of data that is shown on the page, so you don’t have to put too much stress on the DOM tree.

Most UI libraries in React come with a pagination component, but if you want to quickly implement pagination without having to install a UI library, you might want to check out react-paginate. The library renders a pagination component that accepts some props helps you navigate through your data.

To install the library, run the code below in your terminal.

npm i react-paginate

After installation, you can modify your App component to paginate the data instead of rendering it at once. Paste the code below in your App component.

import React, { useState, useEffect } from 'react';
import ReactPaginate from 'react-paginate';
import faker from 'faker'
import './App.css';

function App() {
  const [pagination, setPagination] = useState({
    data: new Array(1000).fill().map((value, index) => (({
      id: index,
      title: faker.lorem.words(5),
      body: faker.lorem.sentences(8)
    }))),
    offset: 0,
    numberPerPage: 10,
    pageCount: 0,
    currentData: []
  });
  useEffect(() => {
    setPagination((prevState) => ({
      ...prevState,
      pageCount: prevState.data.length / prevState.numberPerPage,
      currentData: prevState.data.slice(pagination.offset, pagination.offset + pagination.numberPerPage)
    }))
  }, [pagination.numberPerPage, pagination.offset])
  const handlePageClick = event => {
    const selected = event.selected;
    const offset = selected * pagination.numberPerPage
    setPagination({ ...pagination, offset })
  }
  return (
    <div>
      {pagination.currentData && pagination.currentData.map(((item, index) => (
        <div key={item.id} className="post">
          <h3>{`${item.title} - ${item.id}`}</h3>
          <p>{item.body}</p>
        </div>
      )))
      }
      <ReactPaginate
        previousLabel={'previous'}
        nextLabel={'next'}
        breakLabel={'...'}
        pageCount={pagination.pageCount}
        marginPagesDisplayed={2}
        pageRangeDisplayed={5}
        onPageChange={handlePageClick}
        containerClassName={'pagination'}
        activeClassName={'active'}
      />
    </div>
  );
}
export default App;

In this example, we’re storing the details responsible for the pagination in our paginating state. Instead of rendering the data at once, we only render the current data, which we get by slicing the main data based on the current offset and the number of records to be displayed on the page.

The ReactPaginate component accepts an event handler as a prop, which is called whenever the page changes. The event handler calculates the current offset, which is then used to calculate the current data to display when the page loads.

Below is a screenshot of how the app looks after pagination has been added to it.

List Paginated with react-paginate

2. Infinite scroll

Another way to render a large amount of data is with infinite scroll. Infinite scroll involves appending data to the end of the page as you scroll down the list. When the page initially loads, only a subset of data is loaded. As you scroll down the page, more data is appended.

There are several ways to implement infinite scroll in React. Personally, I prefer to use react-infinite-scroll-component. To install it, run the following code in your terminal.

npm i react-infinite-scroll-component 

Open your App component and paste the following.

import React, { useState } from 'react';
import faker from 'faker'
import InfiniteScroll from 'react-infinite-scroll-component';
import './App.css';

function App() {
  const data = new Array(1000).fill().map((value, id) => (({
    id: id,
    title: faker.lorem.words(5),
    body: faker.lorem.sentences(8)
  })))

  const [count, setCount] = useState({
    prev: 0,
    next: 10
  })
  const [hasMore, setHasMore] = useState(true);
  const [current, setCurrent] = useState(data.slice(count.prev, count.next))
  const getMoreData = () => {
    if (current.length === data.length) {
      setHasMore(false);
      return;
    }
    setTimeout(() => {
      setCurrent(current.concat(data.slice(count.prev + 10, count.next + 10)))
    }, 2000)
    setCount((prevState) => ({ prev: prevState.prev + 10, next: prevState.next + 10 }))
  }

  return (
    <InfiniteScroll
      dataLength={current.length}
      next={getMoreData}
      hasMore={hasMore}
      loader={<h4>Loading...</h4>}
    >
      <div>
        {current && current.map(((item, index) => (
          <div key={index} className="post">
            <h3>{`${item.title}-${item.id}`}</h3>
            <p>{item.body}</p>
          </div>
        )))
        }
      </div>
    </InfiniteScroll>
  );
}
export default App;

Basically what happens here is whenever the user scrolls to the end of the page it checks if the hasMore property is false. If it isn’t it appends more data to the page. It keeps on appending data to the end of the page until the hasMore property becomes false.

3. react-virtualized

react-virtualized was specifically designed for rendering large lists and tabular data. It uses a technique similar to infinite scroll called windowing. With windowing, only the visible parts of a list are rendered to the screen.

One advantage react-virtualized has over the aforementioned solutions is its wealth of useful components, including:

  • Collection
  • Grid
  • List
  • Masonryf
  • Table

To install react-virtualized, launch your terminal and run the following.

npm i react-virtualized

Go to your App component and paste the code below.

import React from 'react';
import faker from 'faker'
import { List } from "react-virtualized";
import './App.css';

function App() {
  const data = new Array(1000).fill().map((value, id) => (({
    id: id,
    title: faker.lorem.words(5),
    body: faker.lorem.sentences(8)
  })))

  const renderRow = ({ index, key, style }) => (
   <div>
    <div key={key} style={style} className="post">
      <h3>{`${data[index].title}-${data[index].id}`}</h3>
      <p>{data[index].body}</p>
    </div>
   </div>
  )
  return (
    <List
      width={1200}
      height={700}
      rowRenderer={renderRow}
      rowCount={data.length}
      rowHeight={120}
    />
  );
}
export default App;

The List component takes the width and height props to set the dimensions of the window. It also takes the rowHeight prop, which represents the height of every item in the list, and rowCount, which represents the length of the array. rowRenderer takes a function that is responsible for rendering each row.

react-virtualized comes with many other options for handling large lists.

4. react-window

react-window is a set of components for efficiently rendering large lists in React. A complete rewrite of react-virtualized, the library aims to address shortcomings related to size and speed. react-window also covers more edge cases than react-virtualized.

Install react-window by running the code below in your terminal.

npm i react-window

Go to your App component and replace the code with the code below.

import React from 'react';
import faker from 'faker'
import { FixedSizeList as List } from "react-window";
import './App.css';

function App() {
  const data = new Array(1000).fill().map((value, id) => (({
    id: id,
    title: faker.lorem.words(5),
    body: faker.lorem.sentences(8)
  })))

  const Row = ({ index, key, style }) => (
   <div>
    <div key={key} style={style} className="post">
      <h3>{`${data[index].title}-${data[index].id}`}</h3>
      <p>{data[index].body}</p>
    </div>
   </div>
  )
  return (
    <List
      width={1400}
      height={700}
      itemCount={data.length}
      itemSize={120}
    >
      {Row}
    </List>
  );
}
export default App;

The code is very similar to that of react-virtualized. We used a List component, which accepts a set of props that defines the list, and passed in a Row component function, which is responsible for rendering each row in the list.

The author of the library outlined the differences between react-window and react-virtualized.

Conclusion

When handling a large list, it’s important not to render all the data at once to avoid overloading the DOM tree. The best approach to improving performance depends on your use case. If you prefer to render all the data in one place, infinite scroll or a windowing technique would be your best bet. Otherwise, you may prefer a pagination solution that segments data into different pages.

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    Full visibility into production React apps

    Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

    LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

    The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

    Modernize how you debug your React apps — .

    Samaila Bala I'm a frontend engineer and technical writer.

    Leave a Reply