What's new in v8
v8 is a full rewrite built around a new headless hook architecture. Every major feature is now
composable and usable independently of <DataTable>. Here's what changed.
Column filtering
A new built-in filter popup per column. Set filterable: true on any column to add a
filter icon to its header. The filter type ("text", "number", "date")
controls the available operators and input widget.
const columns: TableColumn<Row>[] = [
{ name: 'Name', selector: r => r.name, filterable: true, filterType: 'text' },
{ name: 'Salary', selector: r => r.salary, filterable: true, filterType: 'number' },
{ name: 'Hired', selector: r => r.hired, filterable: true, filterType: 'date' },
];
Filters use a structured FilterState with two independent conditions joined by
AND or OR. Supply a custom filterFunction on a column
to override the built-in operator logic.
→ Column filtering docs
Column groups
Span a label across multiple adjacent columns using the new columnGroups prop.
Groups render as a second header row above the regular column headers.
<DataTable
columnGroups={[
{ name: 'Personal', columnIds: ['first', 'last'] },
{ name: 'Employment', columnIds: ['role', 'dept', 'salary'] },
]}
columns={columns}
data={data}
/> Drag-to-reorder columns and groups
Users can drag column headers to reorder them. Set reorder: true on each column
that should be draggable. Column groups can be dragged as a unit too. Set reorder: true
on the group and the entire block moves together.
const columns = [
{ id: 'name', name: 'Name', reorder: true, selector: r => r.name },
{ id: 'dept', name: 'Dept', reorder: true, selector: r => r.dept },
];
<DataTable
columns={columns}
onColumnOrderChange={cols => setColumns(cols)}
data={data}
/> Column visibility hook
useColumnVisibility is a new headless hook for managing a show/hide column picker.
It returns a columns array with omit pre-set. Pass it directly to <DataTable>.
const { columns, visibility, toggle, setAll } = useColumnVisibility(rawColumns); Improved loading state
progressPending now distinguishes between initial load and re-fetch:
- Initial load (no data yet): renders shimmer skeleton rows that match your column layout.
- Re-fetch (data already showing): dims existing rows and overlays a centered spinner, preserving table structure.
The column header always stays visible in both states. → Loading state docs
Row animations
Set animateRows to stagger row entrances and animate sort transitions.
Automatically suppressed when the user has prefers-reduced-motion enabled.
<DataTable animateRows columns={columns} data={data} /> Column separators
Two new props control vertical lines between columns independently for body rows and headers.
<DataTable
columnSeparator="subtle" // body: inset 60%-height line
headerSeparator="full" // header: full-height line
columns={columns}
data={data}
/> Headless hooks
All internal logic is now exposed as composable hooks. Use them to build fully custom table markup while keeping the library's sort, pagination, and filter engines.
| Hook | Purpose |
|---|---|
useColumns | Resolve column definitions and defaults |
useTableState | Manage sort, page, and selection state |
useTableData | Sort, paginate, and slice rows |
useColumnFilter | Per-column filter values and predicate application |
useColumnVisibility | Show/hide column state |
Imperative ref API
Attach a ref to <DataTable> to call imperative methods.
The clearSelectedRows prop is now deprecated in favour of this API.
const ref = useRef<DataTableHandle>(null);
ref.current?.clearSelectedRows();
<DataTable ref={ref} selectableRows columns={columns} data={data} /> New row events
onRowMiddleClicked fires on scroll-click. Use it with onRowClicked
to implement open-in-new-tab behavior.
<DataTable
onRowClicked={row => navigate(`/users/${row.id}`)}
onRowMiddleClicked={row => window.open(`/users/${row.id}`, '_blank')}
columns={columns}
data={data}
/> Upgrading from v7?
See the Migration guide for a full list of breaking changes and how to update your code.