Overview

React Table is a headless UI library for building data tables in React applications. As a headless library, it provides the underlying logic and state management for table features—such as sorting, filtering, pagination, row selection, and column resizing—without rendering any visual components or enforcing specific styling. This approach offers developers maximum flexibility to define the table's appearance and integrate it seamlessly with existing design systems or component libraries.

The library is particularly well-suited for scenarios requiring highly customized and performant data grids. Developers bring their own UI components (e.g., <table>, <thead>, <tbody>, <tr>, <td>) and apply styling using CSS, CSS-in-JS, or utility-first frameworks. This separation of concerns allows for precise control over the user experience and visual design, making it a suitable choice for applications with unique branding requirements or complex interactive data visualization needs. For instance, a financial dashboard might use React Table to display real-time stock data with custom conditional formatting and inline editing capabilities, while an e-commerce platform could implement a product catalog with dynamic filtering and multi-column sorting.

While React Table offers extensive customization, it requires more initial setup compared to opinionated, component-based table libraries. Developers need to explicitly render the table structure and connect it to React Table's hooks and utilities to manage state and behavior. This design choice prioritizes adaptability over out-of-the-box readiness, appealing to teams that require fine-grained control and are comfortable with building UI from foundational elements. The library supports both client-side and server-side data processing, enabling efficient handling of large datasets by delegating filtering, sorting, and pagination logic to a backend API when necessary. This is a common pattern in enterprise applications where performance with extensive data is critical, aligning with architectural principles for scalable web applications as discussed by sources like ThoughtWorks.

React Table (now officially TanStack Table) is maintained as part of the TanStack suite of open-source libraries, which also includes TanStack Query and TanStack Router. Its evolution reflects a commitment to providing fundamental tools for modern web development, emphasizing performance, type safety (with TypeScript support), and a composable API design.

Key features

  • Headless UI Logic: Provides core table functionality (sorting, filtering, pagination, grouping) without rendering any UI, allowing full control over presentation.
  • Column Customization: Supports flexible column definitions, including custom cell rendering, column reordering, resizing, and pinning.
  • Data Filtering: Offers built-in filtering capabilities with various filter types and the option to implement custom filter functions.
  • Data Sorting: Enables single and multi-column sorting with customizable sort functions and indicators.
  • Pagination: Manages client-side and server-side pagination state, allowing navigation through large datasets efficiently.
  • Row Selection: Facilitates single or multiple row selection, useful for bulk actions or detailed views.
  • Column Grouping & Aggregation: Supports grouping rows by column values and performing aggregations on grouped data.
  • Virtualization Support: Designed to integrate with virtualization libraries for rendering large datasets without performance degradation.
  • TypeScript Support: Fully typed API for enhanced developer experience and compile-time error checking.
  • Memoization: Utilizes memoization techniques to optimize re-renders and improve table performance.

Pricing

React Table is distributed under the MIT License, making it free and open source for all uses.

Feature Availability Notes
Core Library Free Provides all headless table logic and hooks.
Commercial Support Not directly offered Community support via GitHub issues and discussions.
License MIT License Permits free use, modification, and distribution.

Pricing as of 2026-05-28. For detailed licensing information, refer to the Open Source Initiative's MIT License page.

Common integrations

  • React: As a React-specific library, it integrates directly with React components and hooks to manage table state and rendering.
  • Tailwind CSS / Styled Components / Emotion: Compatible with any styling solution, as developers provide their own UI components and apply styling directly.
  • React Query (TanStack Query): Often used to fetch and manage data for tables, leveraging its caching and synchronization capabilities.
  • React Router (TanStack Router): Can be integrated for managing URL-based state for table parameters like pagination and filtering.
  • Headless UI: Can be used alongside other headless UI libraries like Headless UI from Tailwind Labs for other component types, maintaining a consistent headless approach.
  • Framer Motion: For adding animations to table elements, such as row transitions or column reordering.

Alternatives

  • AG Grid: A feature-rich JavaScript data grid with extensive capabilities, available in community and enterprise editions.
  • Material-UI Data Grid: A React component that provides a data grid adhering to Material Design principles.
  • React-Table (v7 – legacy): The previous major version of React Table, which has a slightly different API and feature set.
  • Bootstrap Table: A jQuery plugin that enhances standard HTML tables with advanced features.
  • Highcharts Data Grid: A data grid component designed to integrate with Highcharts charting library, offering data visualization and manipulation features.

Getting started

To begin using React Table, install it via npm or yarn. The following example demonstrates a basic table with sorting and filtering capabilities.

import React from 'react';
import { 
  useReactTable, 
  getCoreRowModel, 
  flexRender, 
  getSortedRowModel, 
  getFilteredRowModel 
} from '@tanstack/react-table';

function MyTable() {
  const data = React.useMemo(
    () => [
      { id: 1, name: 'Alice', age: 30, city: 'New York' },
      { id: 2, name: 'Bob', age: 24, city: 'Los Angeles' },
      { id: 3, name: 'Charlie', age: 35, city: 'Chicago' },
      { id: 4, name: 'David', age: 29, city: 'New York' },
    ],
    []
  );

  const columns = React.useMemo(
    () => [
      {
        accessorKey: 'name',
        header: 'Name',
        cell: info => info.getValue(),
        footer: props => props.column.id,
      },
      {
        accessorKey: 'age',
        header: 'Age',
        cell: info => info.getValue(),
        footer: props => props.column.id,
      },
      {
        accessorKey: 'city',
        header: 'City',
        cell: info => info.getValue(),
        footer: props => props.column.id,
      },
    ],
    []
  );

  const [sorting, setSorting] = React.useState([]);
  const [globalFilter, setGlobalFilter] = React.useState('');

  const table = useReactTable({
    data,
    columns,
    state: {
      sorting,
      globalFilter,
    },
    onSortingChange: setSorting,
    onGlobalFilterChange: setGlobalFilter,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    debugTable: true,
  });

  return (
    <div>
      <input
        value={globalFilter ?? ''}
        onChange={e => setGlobalFilter(e.target.value)}
        placeholder="Search all columns..."
      />
      <table style={{ width: '100%', borderCollapse: 'collapse' }}>
        <thead>
          {table.getHeaderGroups().map(headerGroup => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map(header => (
                <th
                  key={header.id}
                  colSpan={header.colSpan}
                  style={{ border: '1px solid #ddd', padding: '8px', textAlign: 'left', cursor: header.column.getCanSort() ? 'pointer' : 'default' }}
                  onClick={header.column.getToggleSortingHandler()}
                >
                  {
                    header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.header,
                          header.getContext()
                        )
                  }
                  {{
                    asc: ' ▲',
                    desc: ' ▼',
                  }[header.column.getIsSorted() ?? null]}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map(row => (
            <tr key={row.id}>
              {row.getVisibleCells().map(cell => (
                <td key={cell.id} style={{ border: '1px solid #ddd', padding: '8px' }}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
        <tfoot>
          {table.getFooterGroups().map(footerGroup => (
            <tr key={footerGroup.id}>
              {footerGroup.headers.map(header => (
                <th key={header.id} colSpan={header.colSpan} style={{ border: '1px solid #ddd', padding: '8px', textAlign: 'left' }}>
                  {
                    header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.footer,
                          header.getContext()
                        )
                  }
                </th>
              ))}
            </tr>
          ))}
        </tfoot>
      </table>
    </div>
  );
}

export default MyTable;

This example sets up a basic table with four columns and four rows of data. It demonstrates how to define columns, use useReactTable to manage table state, and render the table structure using flexRender. It also includes basic global filtering and column sorting functionality, showing how to connect user interactions to the table's state management. For more advanced features and deeper customization, consult the React Table official documentation.