DEV Community

loading...
Cover image for Using a React Table with Client-Side Pagination
React Rainbow

Using a React Table with Client-Side Pagination

leandrotorressicilia profile image LeandroTorresSicilia ・4 min read

If you’d prefer to watch rather than read:
https://youtu.be/v9E7moqUJ40

Pagination is a simple navigation technique that allows you to divide content into pages. It is very useful when you want to display a large recordset on multiple pages, for example in a table with a huge amount of data.

In this article, I’ll be teaching you how to use a React Table with client-side pagination using react-rainbow-components. To accomplish this tutorial, you must have a general background in React, Node.js, and Yarn.

Project setup

What you will need:

  • Node.js
  • Yarn
  • Your favorite IDE (For this tutorial, I will use VSCode)

Add dependencies

For this project, I will use a new create-react-app project. If you want to know more and how to initialize a project, see: https://create-react-app.dev/.

Once in the root of your project, let’s install react-rainbow-components and react-query:

$ yarn add react-rainbow-components react-query
Enter fullscreen mode Exit fullscreen mode

Once you have your new app with all dependencies installed, we should be ready to code!

Coding

Importing dependencies

Now we will start by importing react-query and react-rainbow-components:

import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import { Table, Column } from 'react-rainbow-components';
import './App.css';
Enter fullscreen mode Exit fullscreen mode

Creating the service

Create a function that fetches a list with all the countries:

const API_URL = "https://sampleapis.com/countries/api/countries";
const fetchCountries = async () => {
    const result = await fetch(API_URL);
    return result.json();
};
Enter fullscreen mode Exit fullscreen mode

I am using a free API that will return a list with certain information about the countries like name, capital, population, currency, and more.

Countries Component

Now let’s add a Countries component that will render a Table with all the countries returned by the API call:

function Countries() {
    const { isLoading, data } = useQuery(
        "countries",
        fetchCountries,
    );
    return (
        <Table
            className="countries-table"
            keyField="name"
            isLoading={isLoading}
            data={data}
            variant="listview"
        >
            <Column header="Name" field="name" />
            <Column header="Capital" field="capital" />
            <Column header="Population" field="population" />
            <Column header="Currency" field="currency" />
        </Table>
    );
}
Enter fullscreen mode Exit fullscreen mode

To make the call to the API, I used useQuery hook from the react-query package (https://github.com/tannerlinsley/react-query). This hook will manage the fetch for us and return an object with data, isLoading, error, and more. Thus, we avoid the need to maintain the different states of the fetch process. Also, it provides other advantages like caching, updating out-of-date data in the background, performance optimizations, and many more. If you want to know more about react-query visit its documentation https://react-query.tanstack.com/overview.

The react-rainbow-components Table needs a keyField prop which is required, it can be any field in the data that is unique like an id or a key representing the document or the row in the database. In this case, I used the country name which is unique. The Table also receives an isLoading that will render the loading state when it is true and data that is an array of objects with the data to show in the table. The columns are represented as children of the Table, and they receive a header that is a simple string that will show in the column header. Also, receive a field that represents the key in the data that its value will be shown in the column.

App Component

Next, we will add an app component that is exported and it only has the QueryClientProvider wrapper around the Countries component.

const queryClient = new QueryClient();
function App() {
    return (
        <QueryClientProvider client={queryClient}>
            <Countries />
        </QueryClientProvider>
    );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

The QueryClient is used to interact with the cache managed by react-query. By wrapping your application with the QueryClientProvider you can access the QueryClient anywhere in the app.

Styling

Now we will add some simple styles to the Table component:

.countries-table {
    padding: 40px;
    background-color: rgb(244, 246, 249);
    height: 100%;
    min-height: 100vh;
}
Enter fullscreen mode Exit fullscreen mode

When save and reload the page you will see something like:
Alt Text

You can see it and play in this sandbox:

Problems with this solution

Now we have a large table that shows all countries, but this can cause performance issues since we are rendering too many rows on the page, and creating such an amount of DOM elements is costly.
How to solve this? Here is when client-side pagination comes into play. If your collection or table does not have too many records (hundreds or a few thousand), then you can fetch all of them and do client-side pagination as we will do next.

Client-Side pagination

To achieve the client-side pagination, we can use the TableWithBrowserPagination component from the react-rainbow-components library. The only thing you need to do is import it and pass the pageSize prop with the number of records you want to show per page.

Replace the react-rainbow-components Table import line to:

import { TableWithBrowserPagination, Column } from 'react-rainbow-components';
Enter fullscreen mode Exit fullscreen mode

Here we only change Table for TableWithBrowserPagination.

Next, change the Countries component to use the TableWithBrowserPagination:

function Countries(params) {
    const { isLoading, data } = useQuery(
        "countries",
        fetchCountries,
    );
    return (
        <TableWithBrowserPagination
            className="countries-table"
            keyField="name"
            isLoading={isLoading}
            data={data}
            variant="listview"
            pageSize={15}
        >
            <Column header="Name" field="name" />
            <Column header="Capital" field="capital" />
            <Column header="Population" field="population" />
            <Column header="Currency" field="currency" />
        </TableWithBrowserPagination>
    );
}
Enter fullscreen mode Exit fullscreen mode

There were two simple changes in this code. First, we change Table for TableWithBrowserPagination and add a new prop pageSize with the value of 15, which means that it will render only 15 rows per page.

Now we have:
Alt Text

As you can see, there is pagination at the bottom of the page. Now you can move through the different pages. All the data is already loaded, but we only render 15 rows per page. In this way, we avoid the performance issues caused by creating too many DOM elements.

Conclusion

That’s all folks!

Only with a few lines of code, we have used a table with client-side pagination. The main takeaway is that we can achieve a good user experience by simply splitting the data into pages. Not only does it solve the performance issue, but it also gives us a nice way to represent data since showing a table with a large scroll bar can be annoying to the user.

Thanks for reading!

Discussion (3)

pic
Editor guide
Collapse
preyasprathap profile image
preyas prathap • Edited

Nice. 👍

But on refreshing, the page downloads the entire table data right.

I want to fetch the data only when the user clicks page no ?

Do you know how to do that.

Collapse
leandrotorressicilia profile image
LeandroTorresSicilia Author

Yes refreshing the page downloads all the data. With the TableWithBrowserPagination component right now you can only do client-side pagination, if you want to do server-side pagination, then you could use the Pagination and Table components. Pagination has an onChange prop that receives the selected page number, then knowing the page you can call an API with pagination and get only the page data. Maybe I will write another post using server-side pagination.

Thanks for reading.

Collapse
maxxgreene profile image
Maxx Greene

the use case of doing the pagination 100% on the client apply only for when you know you dataset is small, <2000 records. You don't want you table to render in the DOM 2000 rows then you paginate in the client for performance reasons(less DOM elements)

Fetching that amount of data(<2000) is not a problem today in my opinion. If the use case is other(> 2000 records) then server side pagination :)

Valid to say the 2000 records is a number made my me :) ... it could be more or less