React Table v7
React Table v7 is a set of headless hooks for building highly customizable and performant data tables in React applications. As of version 7.8.0, it provides core functionalities like sorting, filtering, pagination, row selection, and expansion through a flexible plugin system. Its "headless" nature means it provides the logic and state management, leaving all UI rendering and styling entirely up to the developer, offering maximum customization. While `react-table` v7 remains stable and widely used, active development and new features have shifted to the `@tanstack/react-table` package (v8 and beyond). Developers starting new projects are generally advised to use the newer TanStack Table versions, which offer a similar headless approach across various frameworks. This package (v7) is now in a maintenance phase.
Common errors
-
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause Using React Table hooks (e.g., `useTable`) outside of a functional React component, or violating other React Rules of Hooks.fixEnsure all React hooks are called from the top level of your function component, not inside loops, conditions, or nested functions. -
TypeError: Cannot read properties of undefined (reading 'map')
cause Attempting to render table rows or cells before `prepareRow` has been called, or if `data` or `columns` are undefined/null.fixAlways call `prepareRow(row)` for each row before attempting to render it. Verify that your `data` and `columns` arrays are properly initialized and passed to `useTable`. -
Uncaught Error: react-table@7.x.x requires React as a peer dependency.
cause React is not installed or the installed version does not satisfy the `react-table` peer dependency requirement.fixInstall a compatible version of React: `npm install react@^16.8.3` or `npm install react@^17.0.0` or `npm install react@^18.0.0` (or `yarn add`).
Warnings
- breaking React Table v7 is succeeded by `@tanstack/react-table` (v8 and v9), which introduces significant API changes and is published under a new package scope. Migration from v7 to v8/v9 requires substantial code refactoring due to the different hook structures and option definitions.
- deprecated The `react-table` package (v7) is now in maintenance mode. While it remains stable, it will not receive new features or major updates. All active development has shifted to `@tanstack/react-table`.
- gotcha React Table is a 'headless' library, meaning it provides logic and state but no default UI components or styling. Developers must provide all rendering components (table, tr, td, th) and apply their own styling.
- gotcha Incorrectly applying or misordering plugin hooks (e.g., `useSortBy`, `usePagination`) in the `useTable` call can lead to unexpected behavior or runtime errors.
Install
-
npm install react-table -
yarn add react-table -
pnpm add react-table
Imports
- useTable
const useTable = require('react-table')import { useTable } from 'react-table' - useSortBy
import useSortBy from 'react-table'
import { useTable, useSortBy } from 'react-table' - Column
type Column = any
import { Column } from 'react-table'
Quickstart
import React from 'react';
import { useTable, useSortBy, usePagination, Column } from 'react-table';
interface MyData {
id: number;
firstName: string;
lastName: string;
age: number;
visits: number;
status: string;
}
function MyTable() {
const data: MyData[] = React.useMemo(
() => [
{ id: 1, firstName: 'Tanner', lastName: 'Linsley', age: 30, visits: 100, status: 'complicated' },
{ id: 2, firstName: 'Kevin', lastName: 'Van Cott', age: 25, visits: 40, status: 'single' },
{ id: 3, firstName: 'Sarah', lastName: 'Doe', age: 35, visits: 20, status: 'married' },
],
[]
);
const columns: Column<MyData>[] = React.useMemo(
() => [
{ Header: 'ID', accessor: 'id' },
{ Header: 'First Name', accessor: 'firstName' },
{ Header: 'Last Name', accessor: 'lastName' },
{ Header: 'Age', accessor: 'age' },
{ Header: 'Visits', accessor: 'visits' },
{ Header: 'Status', accessor: 'status' }
],
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state: { pageIndex, pageSize },
prepareRow,
} = useTable<
MyData
>(
{
columns,
data,
initialState: { pageIndex: 0, pageSize: 2 },
},
useSortBy,
usePagination
);
return (
<div>
<table {...getTableProps()} style={{ border: '1px solid black', width: '100%' }}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th
{...column.getHeaderProps(column.getSortByToggleProps())}
style={{ borderBottom: 'solid 3px red', background: 'aliceblue', color: 'black', fontWeight: 'bold' }}
>
{column.render('Header')}
<span>
{column.isSorted
? column.isSortedDesc
? ' 🔽'
: ' 🔼'
: ''}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => (
<td
{...cell.getCellProps()}
style={{
padding: '10px',
border: 'solid 1px gray',
background: 'papayawhip',
}}
>
{cell.render('Cell')}
</td>
))}
</tr>
);
})}
</tbody>
</table>
<div style={{ padding: '10px' }}>
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
Previous Page
</button>
<button onClick={() => nextPage()} disabled={!canNextPage}>
Next Page
</button>
<span>
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
</div>
</div>
);
}
export default MyTable;