How to Build React 16+ Data Table with react-table Package

Last updated on by Digamber
In this tutorial, we are going to learn in easy ways how to build data tables in React web application from scratch using react-table package.

How to Build React 16+ Data Table with react-table

If you are beginner, then i will recommend going through following tutorials to get started with react:

Build React MERN Stack CRUD Application

Make HTTP GET, POST Requests with React Axios

How to use React-strap in React Application

Data table is very useful UI component, It helps in managing the complex set of data in any web or mobile application. A user can easily visualize, compare, sort, filter and scan information using a data table.

React 16+ Table Tutorial with Example

We are going to use react-table package to create a data table in React. We will learn to implement essential features of react-table library that helps to show the data in an organized way.

The react-table package is widely used data table third party package for React framework. This library is extremely light and provides almost all the basic table features which are necessary to display the complex information to the users.

It has already got 9k stars on GitHub, and comes with the following features:

  • Filters
  • Resizable
  • Animatable
  • Row Selection
  • Row Expansion
  • Virtualizable
  • Column Ordering
  • Pivoting & Aggregation
  • Sorting (Multi and Stable)
  • Server-side/controlled data/state
  • Extensible via hook-based plugin system
  • Auto out of the box, fully controllable API
  • Headless (100% customizable, Bring-your-own-UI)
  • Lightweight (5kb – 12kb+ depending on features used and tree-shaking)

Setting up React Project

Run the following command to install React app using create-react-app.

npx create-react-app react-table-tutorial

Start the app using below command:

npm start

You can view your app on: localhost:3000

Install react-table in React App

We are going to use the react-table plugin to build the data table component in our React app.

Install React Table, run either of the command based on your package manager:

# NPM
$ npm install react-table

# Yarn
$ yarn add react-table

We also need to install the styled-components using the following command:

styled-components

Build Data Table in React with react-table

Once we are done installing react-table in our React table application. Then we need to import the useTable API at the top of the src/App.js component.

import {useTable} from 'react-table';

Next, import the styled-component package, It allows you to write actual CSS code to style your React or React Native components.

npm i styled-components

Add the code in the App.js component to design our data table.

const Styles = styled.div `
  table {
    width: 100%;
    border-spacing: 0;
    border: 1px solid black;
    tr {
      :last-child {
        td {
          border-bottom: 0;
        }
      }
    }
    th,
    td {
      margin: 0;
      padding: 1rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;
      :last-child {
        border-right: 0;
      }
    }
  }
`

Create Table UI

Next, we will define the Table method and pass columns, data value into it. This method helps in rendering the data table UI, we have also declared the state and methods returned via useTable API to create the data table component in React.

function Table({columns, data}) {
  const {getTableProps, getTableBodyProps, headerGroups, rows, prepareRow} = useTable({columns, data})

  // Render Data Table UI
  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 must have some data that will be rendered by the react-table package in the React data table component.

const data = [
    {
      name: 'Leanne Graham',
      email: 'Sincere@april.biz',
      age: 28,
      status: 'Active'
    },
    {
      name: 'Ervin Howell',
      email: 'Shanna@melissa.tv',
      age: 35,
      status: 'Active'
    },
    {
      name: 'Clementine Bauch',
      email: 'Nathan@yesenia.net',
      age: 33,
      status: 'Inactive'
    },
    {
      name: 'Patricia Lebsack',
      email: 'Julianne@kory.org',
      age: 25,
      status: 'Active'
    },
    {
      name: 'Kamren',
      email: 'Hettinger@annie.ca',
      age: 42,
      status: 'Active'
    },
    {
      name: 'Dennis Schulist',
      email: 'Dach@jasper.info',
      age: 34,
      status: 'Inactive'
    },
    {
      name: 'Kurtis Weissnat',
      email: 'Hoeger@billy.biz',
      age: 44,
      status: 'Active'
    },
    {
      name: 'Maxime_Nienow',
      email: 'Sherwood@rosamond.me',
      age: 26,
      status: 'Active'
    },
    {
      name: 'Glenna Reichert',
      email: 'McDermott@dana.io',
      age: 30,
      status: 'Inactive'
    },                                
  ]

We must define the column directive and ad the column data, add the following code in the App.js file.

The ReactTable will render the data array by mapping the JSON data key associated with column accessor value. For example, our column headers values in table are Name, Email, Age, and Status.

const columns = [
    {
      Header: 'Name',
      accessor: 'name'
    }, {
      Header: 'Email',
      accessor: 'email'
    }, {
      Header: 'Age',
      accessor: 'age'
    }, {
      Header: 'Status',
      accessor: 'status'
    }
]

Inside the return method define the Styles attribute to style the table UI, then set the Table attribute along with table properties to render the JSON data in the React table.

return (
    <Styles>
      <Table
        data={data}
        columns={columns}
      />
    </Styles>
  )

Above code will successfully display the rows of data along with the data values as column header.

Here is how the final App component looks in the src/App.js file:

import React from 'react';
import {useTable} from 'react-table'
import styled from 'styled-components'

const Styles = styled.div `
  table {
    width: 100%;
    border-spacing: 0;
    border: 1px solid black;
    tr {
      :last-child {
        td {
          border-bottom: 0;
        }
      }
    }
    th,
    td {
      margin: 0;
      padding: 1rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;
      :last-child {
        border-right: 0;
      }
    }
  }
`

function Table({columns, data}) {
  const {getTableProps, getTableBodyProps, headerGroups, rows, prepareRow} = useTable({columns, data})

  // Render Data Table UI
  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>
  )
}

function App() {
  const data = [
    {
      name: 'Leanne Graham',
      email: 'Sincere@april.biz',
      age: 28,
      status: 'Active'
    },
    {
      name: 'Ervin Howell',
      email: 'Shanna@melissa.tv',
      age: 35,
      status: 'Active'
    },
    {
      name: 'Clementine Bauch',
      email: 'Nathan@yesenia.net',
      age: 33,
      status: 'Inactive'
    },
    {
      name: 'Patricia Lebsack',
      email: 'Julianne@kory.org',
      age: 25,
      status: 'Active'
    },
    {
      name: 'Kamren',
      email: 'Hettinger@annie.ca',
      age: 42,
      status: 'Active'
    },
    {
      name: 'Dennis Schulist',
      email: 'Dach@jasper.info',
      age: 34,
      status: 'Inactive'
    },
    {
      name: 'Kurtis Weissnat',
      email: 'Hoeger@billy.biz',
      age: 44,
      status: 'Active'
    },
    {
      name: 'Maxime_Nienow',
      email: 'Sherwood@rosamond.me',
      age: 26,
      status: 'Active'
    },
    {
      name: 'Glenna Reichert',
      email: 'McDermott@dana.io',
      age: 30,
      status: 'Inactive'
    },                                
  ]

  const columns = [
    {
      Header: 'Name',
      accessor: 'name'
    }, {
      Header: 'Email',
      accessor: 'email'
    }, {
      Header: 'Age',
      accessor: 'age'
    }, {
      Header: 'Status',
      accessor: 'status'
    }
  ]

  return (
    <Styles>
      <Table
        data={data}
        columns={columns}
      />
    </Styles>
  )

}

export default App

React Table Pagination Example

Tables allow you to gather a large amount of information and display that data in a more organized way. The react-table package is highly scalable and responsive, and It offers you pagination feature to manage the significant amount of data efficiently.

You can manage the large amount of data with usePagination API. Place the following code in the src/App.js file to enable the pagination in React table.

import React from 'react';
import styled from 'styled-components'
import { useTable, usePagination } from 'react-table'

const Styles = styled.div `
  table {
    width: 100%;
    border-spacing: 0;
    border: 1px solid black;
    tr {
      :last-child {
        td {
          border-bottom: 0;
        }
      }
    }
    th,
    td {
      margin: 0;
      padding: 1rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;
      :last-child {
        border-right: 0;
      }
    }
  }
`

function Table({columns, data}) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page, // Instead of using 'rows', we'll use page,
    // which has only the rows for the active page

    // The rest of these things are super handy, too ;)
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize },
  } = useTable(
    {
      columns,
      data,
      initialState: { pageIndex: 0 },
    },
    usePagination
  )

  // Render Data Table UI
  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()}>
          {page.map((row, i) => {
            prepareRow(row)
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map(cell => {
                  return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
                })}
              </tr>
            )
          })}
        </tbody>
      </table>  

     {/* Pagination */}
     <div className="pagination">
        <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
          {'<<'}
        </button>{' '}
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          {'<'}
        </button>{' '}
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          {'>'}
        </button>{' '}
        <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
          {'>>'}
        </button>{' '}
        <span>
          Page{' '}
          <strong>
            {pageIndex + 1} of {pageOptions.length}
          </strong>{' '}
        </span>
        <span>
          | Go to page:{' '}
          <input
            type="number"
            defaultValue={pageIndex + 1}
            onChange={e => {
              const page = e.target.value ? Number(e.target.value) - 1 : 0
              gotoPage(page)
            }}
            style={{ width: '100px' }}
          />
        </span>{' '}
        <select
          value={pageSize}
          onChange={e => {
            setPageSize(Number(e.target.value))
          }}
        >
          {[3, 7, 15].map(pageSize => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>
      </div>
    </>

  )
}

function App() {
  const data = [
    {
      name: 'Leanne Graham',
      email: 'Sincere@april.biz',
      age: 28,
      status: 'Active'
    },
    {
      name: 'Ervin Howell',
      email: 'Shanna@melissa.tv',
      age: 35,
      status: 'Active'
    },
    {
      name: 'Clementine Bauch',
      email: 'Nathan@yesenia.net',
      age: 33,
      status: 'Inactive'
    },
    {
      name: 'Patricia Lebsack',
      email: 'Julianne@kory.org',
      age: 25,
      status: 'Active'
    },
    {
      name: 'Kamren',
      email: 'Hettinger@annie.ca',
      age: 42,
      status: 'Active'
    },
    {
      name: 'Dennis Schulist',
      email: 'Dach@jasper.info',
      age: 34,
      status: 'Inactive'
    },
    {
      name: 'Kurtis Weissnat',
      email: 'Hoeger@billy.biz',
      age: 44,
      status: 'Active'
    },
    {
      name: 'Maxime_Nienow',
      email: 'Sherwood@rosamond.me',
      age: 26,
      status: 'Active'
    },
    {
      name: 'Glenna Reichert',
      email: 'McDermott@dana.io',
      age: 30,
      status: 'Inactive'
    },                                
  ]

  const columns = [
    {
      Header: 'Name',
      accessor: 'name'
    }, {
      Header: 'Email',
      accessor: 'email'
    }, {
      Header: 'Age',
      accessor: 'age'
    }, {
      Header: 'Status',
      accessor: 'status'
    }
  ]

  return (
    <Styles>
      <Table data={data} columns={columns}/>
    </Styles>
  )

}

export default App

React Table pagination needs to have usePagination imported along with useTable API. We implemented the default pagination in React Table that is highly customizable; you can edit and update the Pagination values even the initial Table page index.

Create Responsive Table in React

Now, we are going to learn how to create React Responsive Table using useFlexLayout API, the react-table plugin offers useFlexLayout API and It allows making full-width resizable data table that works flawlessly in every device.

Here is the code for full-width responsive data table example in React:

import React from 'react'
import styled from 'styled-components'
import {
  useTable,
  useResizeColumns,
  useFlexLayout,
  useRowSelect,
} from 'react-table'

const Styles = styled.div`
  padding: 1rem;

  .table {
    ${''}
    display: block;
    ${''}
    overflow: auto;

    border-spacing: 0;
    border: 1px solid black;

    .thead {
      ${''}
      overflow-y: auto;
      overflow-x: hidden;
    }

    .tbody {
      ${''}
      overflow-y: scroll;
      overflow-x: hidden;
      height: 250px;
    }

    .tr {
      :last-child {
        .td {
          border-bottom: 0;
        }
      }
      border-bottom: 1px solid black;
    }

    .th,
    .td {
      margin: 0;
      padding: 0.5rem;
      border-right: 1px solid black;

      ${''}
      position: relative;

      :last-child {
        border-right: 0;
      }

      .resizer {
        right: -5px;
        background: blue;
        width: 10px;
        height: 100%;
        position: absolute;
        top: 0;
        z-index: 1;
        ${''}
        touch-action:none;

        &.isResizing {
          background: red;
        }
      }
    }

    .th {
      &:last-of-type {
        .resizer {
          ${''}
          ${''}
          right: -15px;
        }
      }
    }
  }
`

const headerProps = (props, { column }) => getStyles(props, column.align)

const cellProps = (props, { cell }) => getStyles(props, cell.column.align)

const getStyles = (props, align = 'left') => [
  props,
  {
    style: {
      justifyContent: align === 'right' ? 'flex-end' : 'flex-start',
      alignItems: 'flex-start',
      display: 'flex',
    },
  },
]


function Table({ columns, data }) {
  const defaultColumn = React.useMemo(
    () => ({
      minWidth: 30,
      width: 150,
      maxWidth: 200,
    }),
    []
  )

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
  } = useTable(
    {
      columns,
      data,
      defaultColumn,
    },
    useResizeColumns,
    useFlexLayout,
    useRowSelect,
    hooks => {
    }
  )

  return (
    <div {...getTableProps()} className="table">
      <div>
        {headerGroups.map(headerGroup => (
          <div
            {...headerGroup.getHeaderGroupProps({
              style: { paddingRight: '15px' },
            })}
            className="tr"
          >
            {headerGroup.headers.map(column => (
              <div {...column.getHeaderProps(headerProps)} className="th">
                {column.render('Header')}
                {column.canResize && (
                  <div
                    {...column.getResizerProps()}
                    className={`resizer ${
                      column.isResizing ? 'isResizing' : ''
                    }`}
                  />
                )}
              </div>
            ))}
          </div>
        ))}
      </div>
      <div {...getTableBodyProps()} className="tbody">
        {rows.map((row, i) => {
          prepareRow(row)
          return (
            <div {...row.getRowProps()} className="tr">
              {row.cells.map(cell => {
                return (
                  <div {...cell.getCellProps(cellProps)} className="td">
                    {cell.render('Cell')}
                  </div>
                )
              })}
            </div>
          )
        })}
      </div>
    </div>
  )
}

function App() {
  
  const data = [
    {
      name: 'Leanne Graham',
      email: 'Sincere@april.biz',
      age: 28,
      status: 'Active'
    },
    {
      name: 'Ervin Howell',
      email: 'Shanna@melissa.tv',
      age: 35,
      status: 'Active'
    },
    {
      name: 'Clementine Bauch',
      email: 'Nathan@yesenia.net',
      age: 33,
      status: 'Inactive'
    },
    {
      name: 'Patricia Lebsack',
      email: 'Julianne@kory.org',
      age: 25,
      status: 'Active'
    },
    {
      name: 'Kamren',
      email: 'Hettinger@annie.ca',
      age: 42,
      status: 'Active'
    },
    {
      name: 'Dennis Schulist',
      email: 'Dach@jasper.info',
      age: 34,
      status: 'Inactive'
    },
    {
      name: 'Kurtis Weissnat',
      email: 'Hoeger@billy.biz',
      age: 44,
      status: 'Active'
    }                            
  ]

  const columns = [
    {
      Header: 'Name',
      accessor: 'name'
    }, {
      Header: 'Email',
      accessor: 'email'
    }, {
      Header: 'Age',
      accessor: 'age'
    }, {
      Header: 'Status',
      accessor: 'status'
    }
  ]

  return (
    <Styles>
      <Table columns={columns} data={data} />
    </Styles>
  )
}

export default App

Create React Data Table Sorting

Sorting is a way of arranging the data in ascending or descending order, and It helps to sort the large amount of data (numbers and words) in Table.

We are going to implement the Table sorting functionality in React app using the react-table’ useSortBy API package.

Here is the final code for React data table sorting feature:

import React from 'react'
import styled from 'styled-components'
import { useTable, useSortBy } from 'react-table'

const Styles = styled.div`
  padding: 1rem;

  table {
    border-spacing: 0;
    border: 1px solid black;

    tr {
      :last-child {
        td {
          border-bottom: 0;
        }
      }
    }

    th,
    td {
      margin: 0;
      padding: 0.5rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;

      :last-child {
        border-right: 0;
      }
    }
  }
`

function Table({ columns, data }) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
  } = useTable(
    {
      columns,
      data,
    },
    useSortBy
  )

  // We don't want to render all 2000 rows for this example, so cap
  // it at 20 for this use case
  const firstPageRows = rows.slice(0, 20)

  return (
    <>
      <table {...getTableProps()}>
        <thead>
          {headerGroups.map(headerGroup => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map(column => (
                // Add the sorting props to control sorting. For this example
                // we can add them into the header props
                <th {...column.getHeaderProps(column.getSortByToggleProps())}>
                  {column.render('Header')}
                  {/* Add a sort direction indicator */}
                  <span>
                    {column.isSorted
                      ? column.isSortedDesc
                        ? ' 🔽'
                        : ' 🔼'
                      : ''}
                  </span>
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {firstPageRows.map(
            (row, i) => {
              prepareRow(row);
              return (
                <tr {...row.getRowProps()}>
                  {row.cells.map(cell => {
                    return (
                      <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
                    )
                  })}
                </tr>
              )}
          )}
        </tbody>
      </table>
    </>
  )
}

function App() {
   
  const data = [
    {
      name: 'Leanne Graham',
      email: 'Sincere@april.biz',
      age: 28,
      status: 'Active'
    },
    {
      name: 'Ervin Howell',
      email: 'Shanna@melissa.tv',
      age: 35,
      status: 'Active'
    },
    {
      name: 'Clementine Bauch',
      email: 'Nathan@yesenia.net',
      age: 33,
      status: 'Inactive'
    },
    {
      name: 'Patricia Lebsack',
      email: 'Julianne@kory.org',
      age: 25,
      status: 'Active'
    },
    {
      name: 'Kamren',
      email: 'Hettinger@annie.ca',
      age: 42,
      status: 'Active'
    },
    {
      name: 'Dennis Schulist',
      email: 'Dach@jasper.info',
      age: 34,
      status: 'Inactive'
    },
    {
      name: 'Kurtis Weissnat',
      email: 'Hoeger@billy.biz',
      age: 44,
      status: 'Active'
    },
    {
      name: 'Maxime_Nienow',
      email: 'Sherwood@rosamond.me',
      age: 26,
      status: 'Active'
    },
    {
      name: 'Glenna Reichert',
      email: 'McDermott@dana.io',
      age: 30,
      status: 'Inactive'
    },                                
  ]

  const columns = [
    {
      Header: 'Name',
      accessor: 'name'
    }, {
      Header: 'Email',
      accessor: 'email'
    }, {
      Header: 'Age',
      accessor: 'age'
    }, {
      Header: 'Status',
      accessor: 'status'
    }
  ]

  return (
    <Styles>
      <Table columns={columns} data={data} />
    </Styles>
  )
}

export default App

Conclusion

Finally, we have completed the React Table Tutorial. In this article, we have looked at how to create React Table using the react-table package, and we also learned how to display data beautifully in React app.

Digamber
Digamber

Digamber Rawat is From Uttarakhand, land of Gods located in northwestern part of India. He is a Data Scientist by profession and a primary author of this blog.