How to Make a Table in React using React Table library

Share your love

React table is a library used for creating data tables with data grids which can sometimes be a hassle to make or deal with.

It uses hooks to create powerful tables which are lightweight and extensible, but is headless meaning that it does not have a design of its own, and we are free to design the table in whichever way we see fit. This was done to keep the library light weighted.

It offers us many features like Sorting, Global Filtering, Paginations, Column Filtering, Grouping, etc. You can have a look at all the features on its official site.

We can also make a table in React with the help of hooks only it will also be lightweight and easy to make, but sometimes it is better to use a library than to create something from scratch just to save a ton of time

In this blog, we will only be making a table with the help of React Table Library which can do sorting, filtering, and have pagination in it.

Let’s start…

Index

  1. Getting Started
  2. Creating our React App
  3. Installing the React table library and importing it
  4. Creating the Columns and displaying them
  5. Working on the UI part of the app 
  6. Adding Different Functionalities to make the table more interactive
    1. Sorting
    2. Global Filtering
    3. Pagination
  7. Conclusion

Getting Started

We have prepared dummy data for this project consisting of 100 objects with key and value pairs and will be printing them dynamically from the Data.js file.

dummy data – single object

As React Table is a Headless library, we have to style it ourselves which will not be explained in detail here. But as we have only used Bootstrap for styling, you can use that as a starting point.

Before moving on to the development phase let’s see how our table will look after completion – 

React Table from React Table Library
React Table

This is the live link to the table we will be building and its GitHub repository if you want to try something else.

Creating our React App

It’s easy to create a React App – go to the working directory in any IDE and enter the following command in the terminal:

npx create-react-app react-table-library

If you are unsure how to properly set up a create-react-app project you can refer to the official guide here at create-react-app-dev.‌‌

After the setup, run npm start in the same terminal to start the localhost:3000 where our React app will be hosted. We can also see all our changes there.

Installing React Table Library and Importing it

Like all other npm and yarn packages, installing or adding it is very simple

//For npm
npm install react-table

//For yarn
yarn add react-table 

When the library is finally installed, it’s time to import it into our App.js file.

import React from "react";
import { dummy } from "./Data";
import { useTable } from "react-table";

useTable hook is the main hook of this library as it creates an instance that improves our interaction with the table.

According to the official documentation, “useTable is the primary hook used to build a React Table. It serves as the starting point for every option and every plugin hook that React Table supports.

Creating and Displaying the Columns

useTable Hook has some options which enable it and us to interact with the table. The two most important options that we will be using here are data and columns.

const { instance } = useTable({
  column,
  data,
});

Where instance will be replaced with its different properties depending upon which property we chose to use.

Table Options

Column – This is a required field and should be memorized before passing it to the useTable hook. 

This is the most important part of the UI as it will hold all the table headers and the columns inside it in an object form.

export const tableColumn = [
    {
      Header: "Id",
      accessor: "id",
    },
    {
      Header: "User Name",
      accessor: "name",
    },
    {
      Header: "Email",
      accessor: "email",
    },
    {
      Header: "Comments",
      accessor: "body",
    },
  ];

Data – It is also an important and required field that will contain our dummy data and should also be memorized. 

Column Options

Header – It represents the header that we want each column to have.

Accessor – represents the key values of the dummy data that we made at the beginning of this tutorial. It can contain a string and a function. 

The data returned from this will be by default sortable (but not without using the specific hook).

Every option that we pass to useTable Hook should be memorized as React Table relies on it to determine when state and side effects should update.

Working on the UI part of the app

We have our dummy data and column ready, now let’s work on the UI part of our table.

We will pass the memorized columns and data to useTable Hook. It will return the necessary props for the table, body, and transformed data which will be extracted to create the UI data. 

import React, { useMemo } from "react";
import { dummy } from "./Data";
import { tableColumn } from "./MainTable/Columns";
import { useTable } from "react-table";
const App = () => {
  const columns = useMemo(() => tableColumn, []); //memosized
  const data = useMemo(() => dummy, []); //memosized
 
  const {
    //Instance Props
    getTableProps, //table props from react-table
    getTableBodyProps, //table body props from react-table
    headerGroups, // array that contains all our headers
    prepareRow, // Prepare the row (this function needs to be called for each row before getting the row props)
    rows, //rows for the table based on the table passed
  } = useTable({
    columns,
    data,
  });
  return (
    <>
      <table
        {...getTableProps()}
        className="table table-striped table-hover text-center fs-5 table-bordered border-dark col-12"
      >
        <thead>
          {headerGroups.map((head) => {
            return (
              <>
                <tr {...head.getHeaderGroupProps()}>
                  {head.headers.map((col) => {
                    return (
                      <>
                        <th
                          {...col.getHeaderProps()}
                          className="bg-secondary text-white fs-4"
                        >
                          {col.render("Header")}
                        </th>
                      </>
                    );
                  })}
                </tr>
              </>
            );
          })}
        </thead>
        <tbody {...getTableBodyProps()}>
          {rows.map((row) => {
            prepareRow(row);
            return (
              <>
                <tr {...row.getRowProps()}>
                  {row.cells.map((cell) => {
                    return (
                      <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                    );
                  })}
                </tr>
              </>
            );
          })}
        </tbody>
      </table>
    </>
  );
};
export default App;

Explanation of the above code

The above can be a tough opponent to beat, but this is only true until we understand what each line of the code has to say.

  1. We have wrapped our entire table inside the table tag in which we have passed the getTableProps() functions with a spread operator so that we can extract and use any props inside it in our app.
<table {...getTableProps()}>
 ...
</table>

Thead

  1. We have already been told that headerGroups is an array of all the headers of our app. Thus mapping through it will give us individual headers for our table.
  2. We again have to map an array of headers that were inside headerGroups to get the getHeaderProps function.
  3. By iterating over getHeaderProps(), we will get the value of our headers of the table.
<thead>
  {headerGroups.map((head) => {
    return (
      <>
        <tr {...head.getHeaderGroupProps()}>
          {head.headers.map((col) => {
            return (
              <>
                <th
                  {...col.getHeaderProps()}
                  className="bg-secondary text-white fs-4"
                >
                  {col.render("Header")}
                </th>
              </>
            );
          })}
        </tr>
      </>
    );
  })}
</thead>;

TBody

  1. Similar to the table and thead tag, we will pass the prop getTableBodyProps() in tbody.
  2. We have extracted the value of rows from the instance which will be iterated using the map function to get the data of each field.
  3. For each row, we have to pass a prop – prepareRow() which will make the rendering of the row prop more efficient.
  4. Next, we return <tr> tags to render the row. In each <tr>, we again use map() to parse cells. For each cell, we create a td tag, pass in the prop resolver function getCellProps(), and then render the cell data using the render() method.
<tbody {...getTableBodyProps()}>
  {page.map((row) => {
    prepareRow(row);
    return (
      <>
        <tr {...row.getRowProps()}>
          {row.cells.map((cell) => {
            return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
          })}
        </tr>
      </>
    );
  })}
</tbody>;

The table will look like this after the custom styling 

Simple table UI

Adding Different Functionalities to make the table more interactive

Now, we have our table with a perfect UI of our choice and data up to 100 values but is this enough. 

No, there are many things we as a developer can do, and this library has also a ton of features for us to play with. Let’s use some of the available features and make our table more interactive.

Sorting

One of the best ways to find the data of a client or a user from a table is to sort the whole table alphanumerically and find its name there if you don’t have the filtering option available which we will in the later section.

In the React Table Library, we have hooks for almost any operation that we want to perform in our table. 

For sorting, we have useSortBy hook which we will be importing the right net to the useTable hook.

import { useTable, useSortBy } from "react-table";

And pass the hook as the useTable Hook argument.

const {
  //Instance Props
  getTableProps, //table props from react-table
  getTableBodyProps, //table body props from react-table
  headerGroups, // array that contains all our headers
  prepareRow, // Prepare the row (this function needs to be called for each row before getting the row props)
  rows, //rows for the table based on the table passed
} = useTable(
  {
    columns,
    data,
  },
  useSortBy
);

If you want the sorting to be done by anything other than the default alphanumeric value, you’ll need to update your columns definition with a sort type field. 

export const tableColumn = [
  {
    Header: "Id",
    accessor: "id",
    sort: "baisc"//sort type
  },
  {
    Header: "User Name",
    accessor: "name",
  },
  {
    Header: "Email",
    accessor: "email",
  },
  {
    Header: "Comments",
    accessor: "body",
  },
];

There are three types of sorting that React Table Library allows us to have in our table –

  • alphanumeric – Best for sorting letters and numbers (default)
  • basic – Best for sorting numbers between 0 and 1
  • datetime – Best for sorting by date

For this sorting functionality to work, we have to add a couple of things. 

getSortByTogglrProps() –  Like other props, we have discussed, it is also a required prop to enable column sorting by enabling the toggle feature of the sort between ascending and descending order (depending upon which sort type we have chosen, by default it is alphanumeric) when the user clicks the header of the respective row.

Span tag – this is basically for the UI part of our app as it will tell us when the sorting has started, and in which direction it is currently sorting.

Let’s add them in the header section of the table under th tag

<th
  {...col.getHeaderProps(col.getSortByToggleProps())}
  className="bg-secondary text-white fs-4"
>
  {col.render("Header")}
  <span>
    {col.isSorted // true if the column is sorted at this moment
      ? col.isSortedDesc // for deciding the direction of the sorting
        ? " 🔽" // descending
        : " 🔼" //ascending
      : ""}
  </span>
</th>;

Soritng

We have another Column Option that we can use – disableSortBy.  This option will disable the sorting abilities of a particular column in which it is used.

Filtering

Filtering is considered the most effective way to find any data in the table no matter the type of data. 

React table provides two ways of filtering our table – Column Filtering and Global Filtering.

In this blog, we have only covered Global Filtering.

The process for using every hook from the library is very similar. First, we have to import the hook from the library, and then we have to pass that hook as the argument of the useTable hook.

import { useTable, useSortBy, useFilters } from "react-table";

But all the arguments should be in the same order as we want them in our table. Thus useGlobalFilter Hook will be before useSortBy hook

const {
  //Instance Props
  getTableProps, //table props from react-table
  getTableBodyProps, //table body props from react-table
  headerGroups, // array that contains all our headers
  prepareRow, // Prepare the row (this function needs to be called for each row before getting the row props)
  rows, //rows for the table based on the table passed
} = useTable(
  {
    columns,
    data,
  },
  useGlobalFilter,
  useSortBy
);

Now, let’s work on making this filter an active player in our app. But before that let’s pass some instances required for the global filter to work.

const {
  //Instance Props
  getTableProps, //table props from react-table
  getTableBodyProps, //table body props from react-table
  headerGroups, // array that contains all our headers
  prepareRow, // Prepare the row (this function needs to be called for each row before getting the row props)
  rows, //rows for the table based on the table passed
  setGlobalFilter, //The function used to update the global filter value.
  state: { globalFilter },// initialvalue located on the state object.
} = useTable(
  {
    columns,
    data,
  },
  useGlobalFilter,
  useSortBy
);

Now we are good to go and just have to apply these props in our UI 

<span className="my-3">
  <h3>Search</h3>
  <input
    className="col-2"
    type="text"
    value={globalFilter || ""}
    onChange={(e) => setGlobalFilter(e.target.value)}
  />
</span>;
Filteration

Pagination

At the very beginning of this blog, we said that we will be rendering 100 data in our table.

But these 100 pieces of data, if rendered at the same time will affect our DOM Tree and the efficiency of the app like speed or be more specific load time of our app will decrease.

There are four ways in which we can deal with this problem, and pagination is one of them. Let’s see how to make one using the React Table library.

As always we will first import the usePagination Hook from the library and then also extract some instance properties and table options to use for different features.

import {
  useTable,
  useSortBy,
  usePagination,
  useGlobalFilter,
} from "react-table";
const {
  getTableProps,
  getTableBodyProps,
  headerGroups,
  prepareRow,
  setGlobalFilter, // for global filter
  // rows replaced with page for pagination
  page, //An array of rows for the current page, determined by the pageIndex value.
  nextPage,
  previousPage,
  canPreviousPage, // a boolean value which is true if the pageIndex is not 0
  canNextPage, //a boolean value which is true if the pageIndex is not the last page
  pageOptions,
  gotoPage, // gives us the ability to jump to any page
  state: {
    pageIndex, // Current PageIndex Value
    globalFilter, // current global filter value
  },
} = useTable(
  {
    columns,
    data,
    initialState: {
      pageIndex: 0, // Default value 0
      pageSize: 5, //Determines the amount of rows on any given page. Default Value 10
    },
  },
  useGlobalFilter,
  useSortBy,
  usePagination //should be at the very bottom to avoid errors
);

You might have already noticed that we have replaced rows with page, and now the page will be rendered and mapped through like the rows when we were building our UI in the first section.

But, there is one catch here, this page prop does not have all the rows in it as the rows prop did.

It only has 5 rows in it at a time which is equal to the value of pageSize we have given in the above code which is 5.

Thus we can say that the number of rows rendered through the page will be equivalent to the value of pageSize.

UI for Pagination

<div className="d-flex justify-content-between my-3">
  <button
    className="px-3 py-1 mx-3 text-center btn-secondary"
    onClick={previousPage}
    disabled={!canPreviousPage} //pages before 1 are disabled
  >
    Previous
  </button>
  <span className="fs-4 text-center">
    Page
    <strong className="mx-3">
      {pageIndex + 1} of {pageOptions.length}
    </strong>
    &nbsp; | &nbsp; Go To Page &nbsp;&nbsp;
    <input
      type="number"
      className="col-1 text-center"
      defaultValue={pageIndex + 1}
      onChange={(e) => {
        const pageNumber = e.target.value ? Number(e.target.value) - 1 : 0;
        gotoPage(pageNumber);
      }}
    />
  </span>
  <button
    className="px-3 py-1 mx-3 text-center btn-secondary"
    onClick={nextPage}
    disabled={!canNextPage} //pages after 50 are disabled
  >
    Next
  </button>
</div>;

In our UI of the Pagination Component, we have a button for the next page and previous page which are disabled if they have reached their max limit which is 0, and the last page using their respective props.

A span tag will show us the current page we are on and the last page of the app. And an input field through which we can go to any page number we want simply by using gotoPage prop. It will look something like this at the end.

Pagination

Conclusion

In this blog, we have seen how to make a table in React with the help of a library – React Table Library which can do things like filtering, sorting, and pagination.

You can visit the library to know more about it and try all of its features. You will be amazed to know how much a table built in React with this library can do.

Share your love