Data Tablev1.0.9Ready for Production
Build feature-rich, accessible data tables with sorting, filtering, pagination, row selection, and advanced interactions powered by TanStack Table.
Prefer AI-assisted development?
Catalyst Data Table provides a suite of composable UI components that integrate with TanStack Table v8 — giving you styled, accessible table primitives for sorting, expanding, row/column pinning, column resizing, row selection, and pagination while TanStack Table manages all state and logic.
First Name | Last Name | Age | Visits | Progress |
|---|---|---|---|---|
John | Doe | 28 | 543 | 75 |
Jane | Smith | 34 | 892 | 92 |
Bob | Johnson | 45 | 234 | 45 |
Alice | Williams | 29 | 678 | 88 |
Charlie | Brown | 52 | 156 | 34 |
Diana | Davis | 31 | 789 | 67 |
Eve | Miller | 27 | 445 | 56 |
Frank | Wilson | 38 | 921 | 91 |
Installation
npm install @pmi/catalyst-data-tableNote: @tanstack/react-table (v8.20.6) is a direct dependency bundled with this package — no separate install required.
import { useReactTable, getCoreRowModel, type ColumnDef } from '@tanstack/react-table';
import {
DataTableRoot,
DataTableRow,
DataTableHead,
DataTableCell,
DataTableFlexRender,
} from '@pmi/catalyst-data-table';The Data Table is built on a compound component pattern. The typical JSX structure is:
<DataTableRoot table={table}>
<thead>
<tr>
<DataTableHead header={header}>
<DataTableFlexRender header={header} />
</DataTableHead>
</tr>
</thead>
<tbody>
<DataTableRow row={row}>
<DataTableCell cell={cell}>
<DataTableFlexRender cell={cell} />
</DataTableCell>
</DataTableRow>
</tbody>
</DataTableRoot>Data Table components require a TanStack Table instance created with useReactTable. Enable features by composing row models:
import {
useReactTable,
getCoreRowModel,
getSortedRowModel,
getExpandedRowModel,
getPaginationRowModel,
type ColumnDef,
} from '@tanstack/react-table';
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(), // sorting
getSubRows: (row) => row.subRows, // expanding
getExpandedRowModel: getExpandedRowModel(), // expanding
getPaginationRowModel: getPaginationRowModel(), // pagination
columnResizeMode: 'onChange', // column resizing
});Always wrap data and columns in useMemo to prevent unnecessary re-renders.
Common Patterns
Sorting
Click column headers to toggle ascending, descending, or no sort. The sequence is asc → desc → none.
John | Doe | 28 | 543 | 75 |
Jane | Smith | 34 | 892 | 92 |
Bob | Johnson | 45 | 234 | 45 |
Alice | Williams | 29 | 678 | 88 |
Charlie | Brown | 52 | 156 | 34 |
Diana | Davis | 31 | 789 | 67 |
Eve | Miller | 27 | 445 | 56 |
Frank | Wilson | 38 | 921 | 91 |
Expanding
Click the chevron icon to expand a row and reveal sub-rows. Sub-rows display with adjusted background color reflecting their depth.
expand | First Name | Last Name | Age | Visits | Progress |
|---|---|---|---|---|---|
John | Doe | 28 | 543 | 75 | |
Alice | Williams | 34 | 892 | 92 | |
Charlie | Brown | 45 | 234 | 45 | |
Diana | Davis | 31 | 789 | 67 | |
Frank | Wilson | 38 | 921 | 91 |
Row Selection
Select rows by clicking the checkbox or the row itself. The header checkbox toggles all rows; when some rows are selected it enters an indeterminate state.
First Name | Last Name | Age | Visits | Progress | |
|---|---|---|---|---|---|
John | Doe | 28 | 543 | 75 | |
Jane | Smith | 34 | 892 | 92 | |
Bob | Johnson | 45 | 234 | 45 | |
Alice | Williams | 29 | 678 | 88 | |
Charlie | Brown | 52 | 156 | 34 | |
Diana | Davis | 31 | 789 | 67 | |
Eve | Miller | 27 | 445 | 56 | |
Frank | Wilson | 38 | 921 | 91 |
Row Pinning
Pin rows to the top or bottom of the table. Pinned rows remain sticky during vertical scroll.
📌 | First Name | Last Name | Age | Visits | Progress |
|---|---|---|---|---|---|
John | Doe | 20 | 100 | 10 | |
Jane | Smith | 21 | 117 | 23 | |
Bob | Johnson | 22 | 134 | 36 | |
Alice | Williams | 23 | 151 | 49 | |
Charlie | Brown | 24 | 168 | 62 | |
Diana | Davis | 25 | 185 | 75 | |
Eve | Miller | 26 | 202 | 88 | |
Frank | Wilson | 27 | 219 | 11 | |
John | Doe | 28 | 236 | 24 | |
Jane | Smith | 29 | 253 | 37 | |
Bob | Johnson | 30 | 270 | 50 | |
Alice | Williams | 31 | 287 | 63 | |
Charlie | Brown | 32 | 304 | 76 | |
Diana | Davis | 33 | 321 | 89 | |
Eve | Miller | 34 | 338 | 12 | |
Frank | Wilson | 35 | 355 | 25 | |
John | Doe | 36 | 372 | 38 | |
Jane | Smith | 37 | 389 | 51 | |
Bob | Johnson | 38 | 406 | 64 | |
Alice | Williams | 39 | 423 | 77 |
Column Pinning
Pin columns to the left or right. Pinned columns stay fixed during horizontal scroll.
📌 First Name | 📌 Last Name | 📌 Age | 📌 Visits | 📌 Progress |
|---|---|---|---|---|
John | Doe | 28 | 543 | 75 |
Jane | Smith | 34 | 892 | 92 |
Bob | Johnson | 45 | 234 | 45 |
Alice | Williams | 29 | 678 | 88 |
Charlie | Brown | 52 | 156 | 34 |
Diana | Davis | 31 | 789 | 67 |
Eve | Miller | 27 | 445 | 56 |
Frank | Wilson | 38 | 921 | 91 |
Column Resizing
Drag the resizer handle at a column's edge to adjust its width. Double-click the resizer to reset to the default size.
The resizer button is fully keyboard-operable: focus it with Tab, then use ArrowLeft / ArrowRight to adjust the column width in 10 px steps. Hold Shift while pressing an arrow key to move in 50 px steps. Press Enter to reset the column to its default width. This keyboard behaviour is required for accessibility compliance (WCAG 2.2 SC 2.1.1).
First Name | Last Name | Age | Visits | Progress |
|---|---|---|---|---|
John | Doe | 28 | 543 | 75 |
Jane | Smith | 34 | 892 | 92 |
Bob | Johnson | 45 | 234 | 45 |
Alice | Williams | 29 | 678 | 88 |
Charlie | Brown | 52 | 156 | 34 |
Diana | Davis | 31 | 789 | 67 |
Eve | Miller | 27 | 445 | 56 |
Frank | Wilson | 38 | 921 | 91 |
Pagination
Client-side pagination using TanStack Table's getPaginationRowModel, combined with Catalyst Pagination and Select components.
First Name | Last Name | Age | Visits | Progress |
|---|---|---|---|---|
John | Doe | 20 | 100 | 10 |
Jane | Smith | 21 | 117 | 23 |
Bob | Johnson | 22 | 134 | 36 |
Alice | Williams | 23 | 151 | 49 |
Charlie | Brown | 24 | 168 | 62 |
Diana | Davis | 25 | 185 | 75 |
Eve | Miller | 26 | 202 | 88 |
Frank | Wilson | 27 | 219 | 11 |
John | Doe | 28 | 236 | 24 |
Jane | Smith | 29 | 253 | 37 |
API Reference
Data Table is a compound component built on TanStack Table v8. Each part wraps a native HTML element and accepts a TanStack Table instance to drive its behaviour. The sections below summarize the API surface for each part.
DataTableRoot
Container that links the table UI to the TanStack Table instance. Forwards ref to HTMLTableElement. Extends all standard HTML <table> props.
Prop
Type
DataTableHead
Renders header cells with sorting, pinning, and resizing support. Forwards ref to HTMLTableCellElement. Automatically sets aria-sort based on sort state. Provides data-sorted and data-pinned data attributes for styling. Extends all standard HTML <th> props.
Prop
Type
DataTableRow
Renders rows with selection, expansion, and pinning support. Forwards ref to HTMLTableRowElement. Adds data-selected, data-subrow, and data-pinned data attributes. Extends all standard HTML <tr> props.
Prop
Type
DataTableCell
Renders cells with column-pinning support. Forwards ref to HTMLTableCellElement. Adds data-row-pinned and data-column-pinned data attributes. Extends all standard HTML <td> props.
Prop
Type
DataTableFlexRender
Dynamically renders cell or header content using TanStack Table's flexRender. Forwards ref to HTMLDivElement. Provide either cell or header — not both. Extends all standard HTML <div> props.
Prop
Type
DataTableSorting
Button that toggles column sorting when clicked. Must be used inside DataTableHead. Disabled automatically when the column cannot be sorted. Forwards ref to HTMLButtonElement. Extends all standard HTML <button> props.
Prop
Type
DataTableExpand
Button that toggles row expansion for hierarchical data. Returns null when the row cannot be expanded. Forwards ref to HTMLButtonElement. Adds data-expanded data attribute. Extends all standard HTML <button> props.
Prop
Type
DataTableRowPin
Button that pins or unpins a row to the top or bottom. Returns null when the row cannot be pinned or is already in the requested position. Forwards ref to HTMLButtonElement. Extends all standard HTML <button> props.
Prop
Type
DataTableColumnPin
Button that pins or unpins a column to the left or right. Must be used inside DataTableHead. Returns null when the column cannot be pinned or is already in the requested position. Forwards ref to HTMLButtonElement. Extends all standard HTML <button> props.
Prop
Type
DataTableColumnResizer
Draggable handle for adjusting column width. Must be used inside DataTableHead. Returns null when the column cannot be resized. Double-click to reset to default size. Forwards ref to HTMLButtonElement. Extends all standard HTML <button> props.
Prop
Type
Accessibility
Data Table is designed to meet WCAG 2.2 AA standards through semantic HTML and ARIA attributes.
Keyboard Interactions
| Key | Action |
|---|---|
Tab | Move focus between interactive controls (sort buttons, checkboxes, resizers, pin buttons) |
Enter / Space | Activate the focused interactive control |
ArrowLeft / ArrowRight | When a column resizer is focused: decrease / increase column width by 10 px |
Shift + ArrowLeft / ArrowRight | When a column resizer is focused: decrease / increase column width by 50 px |
Enter (resizer focused) | Reset the column to its default width |
| Arrow keys (other contexts) | Not handled by the component — browser/AT behaviour varies; implement explicit keyboard handling if cell-level arrow navigation is required |
ARIA
DataTableHeadautomatically appliesaria-sort="ascending",aria-sort="descending", oraria-sort="none"on sortable columns (WCAG 2.2 SC 4.1.2).DataTableHeadsetsscope="col"androle="columnheader"on every header cell for correct screen reader table navigation.- All interactive controls (
DataTableSorting,DataTableExpand,DataTableRowPin,DataTableColumnPin,DataTableColumnResizer) render as<button>elements and are keyboard reachable. - Always provide visible labels or
<span className="sr-only">on icon-only controls so screen readers can announce their purpose (e.g."expand row 1","pin column firstName left"). - Row selection checkboxes should carry
aria-labelvalues that identify the row (e.g.aria-label="select row 1").
Choosing the Right Component
| If you need… | Use instead |
|---|---|
| A simple read-only list of items | A plain <ul> or <ol> |
| Lightweight display table with no interaction | Native HTML <table> with Tailwind utilities |
| Paginated navigation controls only | Pagination |
| Filterable option list | Select or Autocomplete |