Handbook
Tables

Effective Tables in React

Last updated by Noel Varanda (opens in a new tab),
React
Tables
TanStack Table
React Data Grid
Headless UI

Implementing tables can be challenging, but there are excellent table libraries available.

Recommendation: For optimal table handling in React, we recommend using TanStack Table (opens in a new tab) due to its comprehensive features and ease of use.

Requirements

  1. Implement advanced functionalities such as sorting, filtering, pagination, and column resizing.

  2. Ensure modularity and reusability by designing a table component that can be easily separated into headers, cells, and footers, and can be used across different tables.

  3. Optimize performance by efficiently rendering large datasets using techniques like virtualization and memoization.

  4. Provide flexibility and customization options for table appearance, behavior, and interaction, including features like column resizing, draggable columns, row selection, and custom cell rendering.

  5. Ensure accessibility (a11y) by adhering to WCAG guidelines, making the table usable and understandable for all users, including those who rely on assistive technologies.

TanStack Table

TanStack Table is a highly regarded table library that has gained popularity in the React community. It offers a comprehensive set of features for creating powerful and customizable tables. Some key features of TanStack Table include:

  • Sorting: Easily sort table columns in ascending or descending order.
  • Filtering: Apply filters to narrow down the displayed data based on specific criteria.
  • Pagination: Split large datasets into smaller pages for improved performance and user experience.
  • Customization: Customize the appearance and behavior of the table components to suit your needs.
  • Accessibility: TanStack Table adheres to accessibility best practices, ensuring your tables are usable by all users.

With TanStack Table, you can quickly set up complex tables with sorting, filtering, pagination, and more, without having to reinvent the wheel. By leveraging a well-maintained and battle-tested library, you can save a significant amount of development time and effort.

These are just some of many examples. For more details and usage examples, refer to the TanStack Table documentation (opens in a new tab).

If we want to create a simple table using react-table, it's as simple as the following snippet:

import { useTable, Column } from '@tanstack/table';
 
type Data = {
  id: number;
  name: string;
  email: string;
};
 
const columns: Column<Data>[] = [
  { key: 'id', header: 'ID' },
  { key: 'name', header: 'Name' },
  { key: 'email', header: 'Email' },
];
 
const data: Data[] = [
  { id: 1, name: 'John Doe', email: 'john.doe@example.com' },
  { id: 2, name: 'Jane Smith', email: 'jane.smith@example.com' },
  // Add more data as needed
];
 
export const MyTable: FC = () => {
  const table = useTable<Data>({ columns, data });
  const { getTableProps, getTableBodyProps, headerGroups, rows } = table;
 
  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) => (
          <tr {...row.getRowProps()}>
            {row.cells.map((cell) => (
              <td {...cell.getCellProps()}>{cell.render('cell')}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
};

Sorting

import { useTable, Column, useSortBy } from '@tanstack/table';
 
const columns: Column<Data>[] = [
  { key: 'id', header: 'ID', sortable: true },
  { key: 'name', header: 'Name', sortable: true },
  { key: 'email', header: 'Email', sortable: true },
];
 
export const MyTable: FC = () => {
  const table = useTable<Data>({ columns, data });
  const { getTableProps, getTableBodyProps, headerGroups, rows } =
    useSortBy(table);
 
  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map((headerGroup) => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map((column) => (
              <th
                {...column.getHeaderProps(column.getSortByToggleProps())}
                className={column.isSorted ? 'sorted' : ''}
              >
                {column.render('header')}
                <span>
                  {column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : ''}
                </span>
              </th>
            ))}
          </tr>
        ))}
      </thead>
      {/* rest of table... */}
    </table>
  );
};

Filtering

import { useTable, Column, useFilters } from '@tanstack/table';
 
export const MyTable: FC = () => {
  const table = useTable<Data>({ columns, data });
  const { getTableProps, getTableBodyProps, headerGroups, rows } =
    useFilters(table);
 
  return (
    <div>
      <input
        type="text"
        placeholder="Search by name"
        onChange={(e) => {
          table.setFilter('name', e.target.value);
        }}
      />
      {/* same table as in original example... */}
    </div>
  );
};

Pagination

import { useTable, Column, usePagination } from '@tanstack/table';
 
export const MyTable: FC = () => {
  const table = useTable<Data>({ columns, data });
 
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageOptions,
    nextPage,
    previousPage,
    state: { pageIndex },
  } = usePagination(table);
 
  return (
    <div>
      <table {...getTableProps()}>
        {/* same thead as in original example... */}
        <tbody {...getTableBodyProps()}>
          {page.map((row) => {
            prepareRow(row);
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell) => (
                  <td {...cell.getCellProps()}>{cell.render('cell')}</td>
                ))}
              </tr>
            );
          })}
        </tbody>
      </table>
      <div>
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          Previous
        </button>
        <span>
          Page{' '}
          <strong>
            {pageIndex + 1} of {pageOptions.length}
          </strong>
        </span>
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          Next
        </button>
      </div>
    </div>
  );
};

Component libraries

TanStack Table is designed to be highly customizable and can be easily integrated with popular component libraries. Let's take an example of integrating TanStack Table with the "AwesomeUI" component library.

We'll use the hypothetical @fe.engineer/components library in the following example. Then, you can create a custom table component that uses the components from "AwesomeUI" while using the underlying functionality of TanStack Table.

import { useTable } from '@tanstack/table';
import {
  TableContainer,
  TableHead,
  TableRow,
  TableCell,
  TableBody,
} from '@fe.engineer/components';
 
export const MyTable: FC = ({ data }) => {
  const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
    useTable<User>({ data, columns });
 
  return (
    <TableContainer>
      <TableHead>
        {headerGroups.map((headerGroup) => (
          <TableRow {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map((column) => (
              <TableCell {...column.getHeaderProps()}>
                {column.render('Header')}
              </TableCell>
            ))}
          </TableRow>
        ))}
      </TableHead>
      <TableBody {...getTableBodyProps()}>
        {rows.map((row) => (
          <TableRow {...row.getRowProps()}>
            {row.cells.map((cell) => (
              <TableCell {...cell.getCellProps()}>
                {cell.render('cell')}
              </TableCell>
            ))}
          </TableRow>
        ))}
      </TableBody>
    </TableContainer>
  );
};
 
export default AwesomeUITable;

Alternative solutions

While React Table is a powerful library for creating tables in React applications, there is an alternative library worth considering based on your specific requirements:

React Data Grid

React Data Grid (opens in a new tab) is a feature-rich library for creating responsive grid layouts in React. It offers a wide range of functionalities, including sorting, filtering, virtualization, column resizing, and much more. React Data Grid is highly customizable and supports both fixed and variable row heights.

Some key features of React Data Grid include:

  • Virtualization: Efficiently renders large datasets by rendering only the visible portion of the grid.
  • Column Resizing: Allows users to resize columns by dragging the column headers.
  • Sorting and Filtering: Provides built-in support for sorting and filtering data based on column values.

Here's an example of how you can use React Data Grid as an alternative to React Table:

import { DataGrid, Column } from 'react-data-grid';
 
type Data = {
  id: number;
  name: string;
  email: string;
};
 
const columns: Column<Data>[] = [
  { key: 'id', name: 'ID' },
  { key: 'name', name: 'Name' },
  { key: 'email', name: 'Email' },
];
 
const data: Data[] = [
  { id: 1, name: 'John Doe', email: 'john.doe@example.com' },
  { id: 2, name: 'Jane Smith', email: 'jane.smith@example.com' },
  // Add more data as needed
];
 
export default function MyTable() {
  return <DataGrid columns={columns} rows={data} />;
}

For more details and usage examples, refer to the React Data Grid documentation (opens in a new tab).

Comparison with TanStack Table

AspectTanStack TableReact Data Grid
VirtualizationBuilt-in supportBuilt-in support
Column ResizingBuilt-in supportBuilt-in support
Sorting and FilteringBuilt-in supportBuilt-in support
Community SupportActive community supportActive community support
TypeScript SupportExcellentExcellent
FlexibilityHighly customizableLimited customization options
PerformanceLightweight and performantEfficient virtualization for large datasets

When to use: React Data Grid excels in rendering extensive data grids with optimized performance, smooth scrolling, and responsiveness. On the other hand, React Table from TanStack offers a more customizable solution, prioritizing flexibility and customization for specific requirements beyond table rendering.

Additional Resources


Keep up to date with any latest changes or announcements by subscribing to the newsletter below.