Paramanantham Harrison Web and mobile app developer. Love exploring the depth of JS fullstack development. React, Vue, React Native, Next JS, and GraphQL are my current love interests. Find me online at learnwithparam.com.

React Table: A complete tutorial with examples

13 min read 3722

React Table: A Complete Tutorial With Examples

Editor’s note: This React Table tutorial was last updated in January 2021 to describe updates included with react-table v7.

Table UIs are very common in web products because it’s one of the most efficient ways to organize complex data in the UI.

Building a table UI from scratch can be a tall order, and React tables, in particular, is known to give developers headaches. Fortunately, there is a wide variety of tools and libraries available to make the experience of creating a React table much simpler and more rewarding — most notably, React Table.

In this tutorial, we’ll show you how to build a smart React data table UI with basic sorting and searching functionalities using react-table.

We’ll cover the following in detail:

What are React tables used for?

Some common use cases for React table UIs include displaying data for financial reports, sports leaderboards, and pricing and comparison pages, to name just a few.

Data Table UI Showing Sports Statistics
Data table showing sports statistics.

Some products that use tables extensively include:

  • Airtable
  • Asana List View
  • Asana Timeline
  • Google Sheets
  • Notion Table

Among the tech giants that use React Table are Google, Apple, and Microsoft.

What is React Table?

React Table is one of the most widely used table libraries in React. It has more than 13,000 stars on GitHub at the time of writing, receives frequent updates, and supports Hooks. The react-table library is very lightweight and offers all the basic features necessary for any simple table.

React Table v7

In March 2020, React Table creator Tanner Linsley released React Table v7, which he described as “the culmination of over a years [sic] worth of work to refactor the entire library to a hooks-only UI/Style/Markup agnostic table building utility.”

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

React Table v7 is comprised of a collection of React Hooks and plugins designed to help you compose logical features of complex data grids into a single, performant, extensible, and unopinionated API, which is returned by the primary useTable hook.

As a headless utility, React Table v7 doesn’t render or supply data table UI elements out of the box. That means you’re responsible for rendering your own table markup using the state and callback of the hooks provided by React Table.

Check out our comprehensive guide to building and stying tables with react-table v7 for a closer look at what’s new in the most recent stable react-table release, or read on to learn how to render your own React table component with react-table.

When to use react-table

Use React Table when your table UI needs:

  • Basic features like sorting, filtering, and pagination
  • Custom UI design for the table without affecting functionality
  • Easy extensibility. You can build your own features on top of the library using custom plugin Hooks

When not to use react-table

Consider another React data table library when you need:

  • Default support for fixed headers and columns
  • Out-of-the-box support for horizontal and vertical scroll for both touch and non-touch devices. react-table doesn’t dictate the UI; it’s headless, so it’s our responsibility to define the UI based on our need
  • Support for inline editing of columns. We can achieve it in react-table, but it’s out of scope for our table to do it. We need to create a plugin or component on top of it to support such features. react-table stays true to its name and is best for rendering simple tables
  • Infinitely long tables like a Google Sheet. Performance-wise, it can’t handle such a large list; it works well for medium-sized tables but not for long ones

Use cases for react-table

  • For simple tables that need basic features like searching, sorting, filtering, etc.
  • Sports leaderboards/statistics, finance data table with custom elements

In this React table tutorial, we’ll demonstrate how to build a simple Airtable clone using react-table. But first, let’s quickly review the features of a fully functional React table UI and discuss some common challenges associated with building React data tables/

React table UI features

Basic features of a React data table UI include:

  • Uncompromised UX and UI. Clearly understandable typography and custom elements inside the table UI
  • Remote data calls to load data
  • Searching the table or specific columns
  • Basic filtering and sorting options

Advanced features in a React data table UI include:

  • Custom sorting and filtering options for columns based on data types (numbers, string, boolean, select input, etc.)
  • Pagination support or infinitely long tables (performance for very large datasets)
  • Showing and hiding columns
  • Support for inline editing of data for a column
  • Support for editing a complete row of data through a modal/details panel
  • Fixed headers and fixed columns for easy viewing of data
  • Support for multiple devices (responsiveness)
  • Resizable columns to accommodate long data points inside a column (e.g., multi-line comments)
  • Horizontal and vertical scroll support
  • Expandable rows to show complete data about the row

Common UX challenges in a React table UI

UI-wise, data tables are one of the best options to show complex data in an organized way. But UX-wise, it’s tricky — it can easily get out of hand when you support multiple devices. Some of the UX challenges for tables include:

Responsiveness

It’s difficult to make a table responsive without changing the layout to suit smaller screen sizes.

Scrolling

A table might need scrolling in both directions. Default browser scrollbars will work well for full-width tables, but most are of a custom width. Custom scrollbars are very tricky to support on both touch and non-touch screens.

Managing column width

Managing the width of the column based on data length is tricky. It often causes UX glitches when we load dynamic data in the table. Each time the data changes, it resizes the column width and causes an alignment glitch. We need to be careful in handling those issues while designing the UX.

When to build your own React table UI

Scenarios in which you might want to build your own React  table UI include:

  • When your table is just a showcase that doesn’t have many interactions
  • When you need a custom UI for the table
  • When you need your table to be very lightweight without any functionality

Here are some common use cases for building your own React table UI, to name just a few:

  • Product/marketing pages with tables for comparison
  • Pricing tables
  • Simple tables with custom styling that don’t require many interactions for the columns other than simple popover text

React Table example: Building a React table component with react-table

Enough theory — let’s dive into a real React Table example. To demonstrate how to use react-table to create a React table component, we’ll build a simple table UI with basic functionalities such as sorting and searching. Here is the React table example we’ll be working with.

First, create a React app using create-react-app:

npx create-react-app react-table-demo

Calling an API with Axios

This is the API endpoint. We are going to call and fetch the shows’ information with the search term “snow” using Axios.

In order to call the API, let’s install axios:

yarn add axios
// App.js

import React, { useState, useEffect } from "react";

import Table from "./Table";
import "./App.css";

function App() {
  // data state to store the TV Maze API data. Its initial value is an empty array
  const [data, setData] = useState([]);

  // Using useEffect to call the API once mounted and set the data
  useEffect(() => {
    (async () => {
      const result = await axios("https://api.tvmaze.com/search/shows?q=snow");
      setData(result.data);
    })();
  }, []);

  return (
    <div className="App"></div>
  );
}

export default App;

We create a state called data, and once the component gets mounted, we call the API using Axios and set the data.

Adding react-table to your app

To add react-table:

yarn add react-table

react-table uses React Hooks. It has a main table Hook called useTable, and it has a plugin system to add plugin Hooks. Thus, react-table is easily extensible based on our custom need.

Let’s create the basic UI with the useTable Hook. We will create a new Table component that will accept two props: data and columns. data is the data we got through the API call, and columns is the object to define the table columns (headers, rows, how the row will be shown, etc.). We will see it in code shortly.

// Table.js

export default function Table({ columns, data }) {
// Table component logic and UI come here
}
// App.js
import React, { useMemo, useState, useEffect } from "react";

import Table from "./Table";

function App() {

  /* 
    - Columns is a simple array right now, but it will contain some logic later on. It is recommended by react-table to memoize the columns data
    - Here in this example, we have grouped our columns into two headers. react-table is flexible enough to create grouped table headers
  */
  const columns = useMemo(
    () => [
      {
        // first group - TV Show
        Header: "TV Show",
        // First group columns
        columns: [
          {
            Header: "Name",
            accessor: "show.name"
          },
          {
            Header: "Type",
            accessor: "show.type"
          }
        ]
      },
      {
        // Second group - Details
        Header: "Details",
        // Second group columns
        columns: [
          {
            Header: "Language",
            accessor: "show.language"
          },
          {
            Header: "Genre(s)",
            accessor: "show.genres"
          },
          {
            Header: "Runtime",
            accessor: "show.runtime"
          },
          {
            Header: "Status",
            accessor: "show.status"
          }
        ]
      }
    ],
    []
  );

  ...

  return (
    <div className="App">
      <Table columns={columns} data={data} />
    </div>
  );
}

export default App;

Here in the columns, we can create multiple groups of headers and columns. We created two levels.

All the columns also have an accessor, which is the data we have in the data object. Our data is inside the show object in the array — that’s why all our accessors have show. as a prefix.

// sample data array looks like this

[
  {
    "score": 17.592657,
    "show": {
      "id": 44813,
      "url": "http://www.tvmaze.com/shows/44813/the-snow-spider",
      "name": "The Snow Spider",
      "type": "Scripted",
      "language": "English",
      "genres": [
        "Drama",
        "Fantasy"
      ],
      "status": "In Development",
      "runtime": 30,
      "premiered": null,
      "officialSite": null,
      "schedule": {
        "time": "",
        "days": [

        ]
      }
      ...
  },
  {
    // next TV show
  }
...
]

Let’s finish our Table component:

// Table.js

import React from "react";
import { useTable } from "react-table";

export default function Table({ columns, data }) {
  // Use the useTable Hook to send the columns and data to build the table
  const {
    getTableProps, // table props from react-table
    getTableBodyProps, // table body props from react-table
    headerGroups, // headerGroups, if your table has groupings
    rows, // rows for the table based on the data passed
    prepareRow // Prepare the row (this function needs to be called for each row before getting the row props)
  } = useTable({
    columns,
    data
  });

  /* 
    Render the UI for your table
    - react-table doesn't have UI, it's headless. We just need to put the react-table props from the Hooks, and it will do its magic automatically
  */
  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <th {...column.getHeaderProps()}>{column.render("Header")}</th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

We will pass the columns and data to useTable. The Hook will return the necessary props for the table, body, and transformed data to create the header and cells. The header will be created by iterating through headerGroups, and the rows for the table body will be created by looping through rows.

{rows.map((row, i) => {
  prepareRow(row); // This line is necessary to prepare the rows and get the row props from react-table dynamically

  // Each row can be rendered directly as a string using the react-table render method
  return (
    <tr {...row.getRowProps()}>
      {row.cells.map(cell => {
        return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
      })}
    </tr>
  );
})}

In this way, we rendered the cell and the header. But our cell values are just strings, and even the array values get converted into comma-separated string values. For example:

// Genres array

show.genres = [
 'Comedy',
 'Sci-fi',
]

In the table, it will simply render as a comma-separated string, e.g., Comedy,Sci-fi. At this point, our app should look like this:

Basic Table UI With Limited Functionality

Custom styling in react-table

This is a fine table for most use cases, but what if we need custom styles? react-table allows you to define custom styles for each cell. It can be defined like this in the column object. Let’s create a badge-like custom element to show each genre.

// App.js

import React, { useMemo } from "react";
...

// Custom component to render Genres 
const Genres = ({ values }) => {
  // Loop through the array and create a badge-like component instead of a comma-separated string
  return (
    <>
      {values.map((genre, idx) => {
        return (
          <span key={idx} className="badge">
            {genre}
          </span>
        );
      })}
    </>
  );
};

function App() {
  const columns = useMemo(
    () => [
      ...
      {
        Header: "Details",
        columns: [
          {
            Header: "Language",
            accessor: "show.language"
          },
          {
            Header: "Genre(s)",
            accessor: "show.genres",
            // Cell method will provide the cell value; we pass it to render a custom component
            Cell: ({ cell: { value } }) => <Genres values={value} />
          },
          {
            Header: "Runtime",
            accessor: "show.runtime",
            // Cell method will provide the value of the cell; we can create a custom element for the Cell        
            Cell: ({ cell: { value } }) => {
              const hour = Math.floor(value / 60);
              const min = Math.floor(value % 60);
              return (
                <>
                  {hour > 0 ? `${hour} hr${hour > 1 ? "s" : ""} ` : ""}
                  {min > 0 ? `${min} min${min > 1 ? "s" : ""}` : ""}
                </>
              );
            }
          },
          {
            Header: "Status",
            accessor: "show.status"
          }
        ]
      }
    ],
    []
  );

  ...
}

...

In the example above, we access the value through the Cell method and then return either the computed value or custom component.

For Runtime, we compute the number of hours and return the custom value. For Genres, we loop and send the value to a custom component, and that component creates a badge-like element.

It’s very easy to customize the look and feel in react-table. After this step, our table UI will look like this:

Table UI With Badges For The Genre Column

In this way, we can customize the style for each cell based on the need. You can show any custom element for each cell based on the data value.

Let’s add a bit more functionality to our table. If you look at the demo page for react-table, it already provides everything you need to create a custom smart table. Just one thing is missing in its demo: global search functionality. So I decided to create that using the useFilters plugin Hook from react-table.

First, let’s create a search input in Table.js:

// Table.js

// Create a state
const [filterInput, setFilterInput] = useState("");

// Update the state when input changes
const handleFilterChange = e => {
  const value = e.target.value || undefined;
  setFilterInput(value);
};

// Input element
<input
  value={filterInput}
  onChange={handleFilterChange}
  placeholder={"Search name"}
/>

It’s a straightforward, simple state to manage the input state. But now, how to pass this filter value to our table and filter the table rows?

For that, react-table has a nice Hook plugin called useFilters.

// Table.js

const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    setFilter // The useFilter Hook provides a way to set the filter
  } = useTable(
    {
      columns,
      data
    },
    useFilters // Adding the useFilters Hook to the table
    // You can add as many Hooks as you want. Check the documentation for details. You can even add custom Hooks for react-table here
  );

In our example, we are going to set the filter only for the Name column. In order to filter the name, when the input value changes, we need to set our first param as the column accessor or ID value and our second param as the search filter value.

Let’s update our handleFilterChange function:

const handleFilterChange = e => {
  const value = e.target.value || undefined;
  setFilter("show.name", value); // Update the show.name filter. Now our table will filter and show only the rows which have a matching value
  setFilterInput(value);
};

This is how the UI looks after the search implementation:

Table UI With Search Functionality

This is a very basic example for filters, and there are several options provided by the react-table API. You can check out the API documentation here.

Adding sorting with useSortBy

Let’s implement one more basic functionality for our table: sorting. Let’s allow sorting for all columns. Again, it’s very simple — same as for filtering. We need to add a plugin Hook called useSortBy and create the style to show the sorting icon in the table. It will automatically handle the sorting in ascending/descending orders.

// Table.js

const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    setFilter
  } = useTable(
    {
      columns,
      data
    },
    useFilters,
    useSortBy // This plugin Hook will help to sort our table columns
  );

// Table header styling and props to allow sorting

<th
  {...column.getHeaderProps(column.getSortByToggleProps())}
  className={
    column.isSorted
      ? column.isSortedDesc
        ? "sort-desc"
        : "sort-asc"
      : ""
  }
>
  {column.render("Header")}
</th>

Based on the sorting, we add the class names sort-desc or sort-asc. We also add the sorting props to the column header.

{...column.getHeaderProps(column.getSortByToggleProps())}

This will automatically allow sorting for all columns. You can control that by disabling sorting in specific columns by using the disableSortBy option on a column. In our example, we allowed sorting on all columns. You can play around with the demo.

This is how the UI looks after our sorting implementation:

Table UI With Sorting Functionality

Of course, you can extend this demo even further — let me know if you need help in the comments section. Some ideas to extend it include:

  • Filter multiple columns using a global filter (Hint: Use setAllFilters instead of setFilter)
  • Create pagination and call more data to load for the table
  • Allow sorting only for specific fields (disable sortby for columns)
  • Instead of passing a hardcoded search value to TV Maze API, create an input to search the TV Maze API directly (i.e., remove client-side filtering and add server-side searching of TV shows through the API and change data)

Check out react-table’s extensive example page to extend this demo. It has a very good kitchen sink to play around with, and it provides solutions for most use cases.

React Table alternatives

Although react-table is the most popular React table library, it’s not always the best solution for building tables in React. there are plenty of alternatives that might also suit your needs, depending on your particular project and use case.

Below are some alternative React table libraries that are worth checking out.

1. react-data-grid

react-data-grid is used for creating smart tables. It has more than 4,000 GitHub stars and is well-maintained.

When to use react-data-grid

Use react-data-grid when your data table needs:

  • Basic features like grouping columns, sorting, searching, and filtering
  • Inline editing of columns
  • A dropdown inside a column (like Google Sheets) or any custom input elements inside the column
  • Support for expanding columns to show more data
  • To be fine-tuned for performance, i.e., it supports virtual rendering for infinitely long table rows
  • Support for empty state when there are no rows

When not to use react-data-grid

react-data-grid covers almost all the basic needs for a data table. However, it doesn’t support pagination by default, so if your table requires pagination, you need to manually implement and handle it. By default, react-data-grid supports longer table UIs and is optimized for performance, so pagination might not be necessary unless the UX demands it.

It also uses Bootstrap for styling. You can still use react-data-grid without it, but then you’d need to add your own styling for the table. It’s not easily customizable compared to react-table, which allows you to create the table structure. Here in react-data-grid, the library creates the table UI, so it’s not great for UI-heavy custom pages.

While the above points aren’t exactly shortcomings, they’re nice to know about before you start using react-data-grid.

react-data-table use cases

Intermediate needs when you have to build a mini editable data table similar to Google Sheets or Airtable with nice UX.

2. react-datasheet

react-datasheet is similar to react-data-grid. It has around 4,500 GitHub stars and is likewise a well-maintained library.

It primarily focuses on helping you create your own Google Sheets-like application. It has basic features built in to create such UX-heavy applications. Once again, it might not be suitable for creating a general-purpose page UI with tables.

Unlike react-data-grid, however, it is not optimized for large datasets, so use it for small applications that need Sheets-like functionality. It has only this one use case, and its features are very limited compared to those of react-data-grid.

3. react-virtualized

As the name implies, react-virtualized is heavily optimized for performance when the dataset is large. This library is not exactly a table library; it does much more. It is exclusively for displaying large datasets on the UI in different formats, like grid, table, and list.

When to use react-virtualized

When your dataset is very large, rendering performance is the key metric for the table; if that’s the case, go for react-virtualized. For normal use cases, this library would be overkill, and the API would be too advanced.

react-virtualized use cases

Use react-virtualized for custom timelines, charts involving infinitely long calendars, and heavy UI elements for your large dataset.

How to choose a React table library

  • For a simple page with limited data, custom styles, and minimum interactivity like sorting and filtering, use react-table
  • To build a mini Google Sheets-like application, but with limited data, use react-data-grid or react-datasheet
  • For a Google Sheets- or Airtable-like application with a large dataset, use react-data-grid
  • When you’re working with a very large dataset and you need a custom UI that offers tables, grids, and many more options, choose reactvirtualized

Conclusion

This is how the final React Table demo looks after we added sorting. You can play around with the demo and check out the codebase for our React Table example.

We have learned how to build a table UI using React. It’s not difficult to create your own table for basic use cases, but make sure you’re not reinventing the wheel wherever possible. Hope you enjoyed learning about table UIs — let me know about your experience with tables in the comments.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard 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 — .

Paramanantham Harrison Web and mobile app developer. Love exploring the depth of JS fullstack development. React, Vue, React Native, Next JS, and GraphQL are my current love interests. Find me online at learnwithparam.com.

12 Replies to “React Table: A complete tutorial with examples”

  1. “react–table is one of the most widely used table libraries in React.”… yet somehow I found two bugs in the first two minutes of playing with it – https://github.com/tannerlinsley/react-table/issues/created_by/dandv

    I don’t know why we keep reinventing the wheel. To repeat the same mistakes? There have been open source table components for over 10 years now. And of course, there’s a much larger crop of React table libraries than the 3 covered in this article. Material-ui, Autodesk, Vaadin, Blueprint all have React-compatible tables, and https://github.com/brillout/awesome-react-components#table–data-grid lists a ton more.

    “When to build your own table UI” should be “pretty much never”.

  2. Thanks for writing this article. In my current app, I use ag-Grid community edition and Tabulator. Both provide inline editing which is a must for me. Unfortunately ag-Grid community edition doesn’t provide row grouping and tree view which I sometimes need. Those features are only available in enterprise edition. So I use Tabulator : http://tabulator.info/, whenever I need row grouping and tree view. There is a React version of Tabulator.
    However, I am now concerned with the bundle size. Both libraries are big, especially ag-Grid. So recently I have been searching other options. React-Data-Grid looked nice, but when I saw the size of my app after adding a module that used it, I was surprised : my app size increased by about 10Mb. I also tried Reac-Table which was satisfactory in terms of the bundle size only, but it’s unlikely I will use it in my current app. I need some experiment of it prior to using it in a real project. Besides, I still lack of skill in using Hooks.
    I am quite tired searching for alternatives of ag-Grid community edition and Tabulator which have been serving me well apart of their bundle size.
    I am still wondering what caused such a huge bundle size increase when I used React-Data-Grid. Maybe you know the reason.

  3. Very nice article. Thank you Paramanantham. A couple small contributions: App.js is missing: import axios from ‘axios’; I also had problems with “yarn add” not installing things correctly. Fixed by switching to “npm install”. When I finally got the table to show up, there were no borders around the rows and columns like yours showed, nor was every other row shaded. Didn’t look as nice. Why?

  4. For Alternating table colors

    Add a table.css file
    make import Table.css into Table.js
    Add this code below

    table {
    border-collapse: collapse;
    width: 100%;
    }

    th, td {
    text-align: left;
    padding: 8px;
    }

    tr:nth-child(even) {background-color: #f2f2f2;}

  5. I am having the same issue with the lack of borders, also his code here:

    const Genres = ({ values }) => {
    // Loop through the array and create a badge-like component instead of a comma-separated string
    return (

    {values.map((genre, idx) => {
    return (

    {genre}

    );
    })}

    );
    };

    Does not have proper syntax.

  6. Well actually i was told to create a DataTable component, not to use an existing one, so for me it make sense that he share this knwoledge with us, though i agree with you there’re lot of libraries that we can use, but dependens on client also or that’s my particular case…

  7. thank you for this awesome article. really help.

    please , is there a way you can do vertical column header in react-table and also add pagination.

    i am having issue connecting pagination with this table you build in this tutorial.

    thank you.

  8. Hey,
    Can you please show how to implement setAllFilters([]). I do not want to use globalFilter, as I am looking to filter only two columns using the filter textfield provided in your example.
    There is no example online that shows how to use setAllFilters instead of setFilter. Also, the important thing is I want to use the same filterInput for both columns.

    Thanks !!

  9. Did you find a way to do this? I’m having problems with the vertical column header thing

Leave a Reply