October 2026 - Data Table Client Mode
Client mode and per-column URL filters for Data Table; Data Grid now identifies cells by row id. Includes breaking changes.
Data Table and Data Grid are synced with tablecn, which is now the source for both components. This release has breaking changes. Read the migration notes before reinstalling.
Data Table
Breaking changes
-
Renamed components. Reinstall under the new registry names. The old names no longer resolve.
Before After DataTableSortList(data-table-sort-list)DataTableSortMenu(data-table-sort-menu)DataTableFilterList(data-table-filter-list)DataTableFilterMenu(data-table-filter-menu)DataTableFilterMenu(data-table-filter-menu)DataTableCommandFilterMenu(data-table-command-filter-menu)The name
DataTableFilterMenunow refers to the filter list. If you used the command-style menu, switch toDataTableCommandFilterMenu. -
One filter model.
enableAdvancedFilteris removed fromuseDataTable.DataTableToolbarandDataTableAdvancedToolbarnow read and write the same filters, so you no longer pick a mode in the hook. -
Per-column URL filters. Filters no longer live in a single JSON
filtersparam. Each filterable column gets its own param, keyed by column id, with the operator as a prefix:?title=ilike.report&status=in.todo,done. Filters that use a column's default operator drop the prefix:?title=report. Links saved with the oldfiltersparam stop applying their filters. Sort links in the old JSON format still parse. -
queryKeys.filtersis removed.queryKeysnow acceptspage,perPage,sort, andjoinOperator. -
Menu props.
DataTableFilterMenuandDataTableCommandFilterMenuno longer takedebounceMs,throttleMs, orshallow; set those onuseDataTable. The filter, sort, and view menus acceptdisabledand forward popover content props such asalign. -
Renamed types and files.
lib/parsersis replaced bylib/data-table-parsers, anddataTableConfigis replaced by theFILTER_VARIANTS,FILTER_OPERATORS, andJOIN_OPERATORSconstants inlib/data-table-utils.Before After ExtendedColumnFilter<TData>ColumnFilterItem<TColumnId>ExtendedColumnSort<TData>ColumnSortItem<TColumnId>OptionFilterOptionQueryKeysDataTableQueryKeysgetFiltersStateParsergetColumnFilterParser -
Memoized cells. Rows and cells only re-render when the state they subscribe to changes. A custom checkbox column that reads
row.getIsSelected()no longer updates when clicked. UsegetDataTableSelectColumn()fromdata-table-select-column, or wrap selection-dependent cells inSubscribe. -
Tableprops are typed asReactTable. Components taketable: ReactTable<DataTableFeatures, TData, unknown>, the typeuseDataTablealready returns. Code that passes the hook'stablestraight through doesn't change.
New
-
Client mode. Pass
mode: "client"to sort, filter, and paginatedatain the browser, with nopageCount. Server mode stays the default and still requirespageCount.const { table } = useDataTable({ mode: "client", data, columns, }); -
Server helpers.
getDataTableSearchParamsbuilds the nuqs parsers from a column config, andgetDataTableQueryturns the parsed search params into{ page, perPage, sorting, filters, joinOperator }for your database query.const columnConfigs = { title: { variant: "text" }, status: { variant: "multiSelect" }, createdAt: { variant: "dateRange" }, } satisfies DataTableColumnConfigs; const searchParams = getDataTableSearchParams({ columnConfigs }); -
Right-to-left. Layout, pinning, and menus follow
DirectionProvider. -
Accessibility. Fixes from an accessibility audit, and a
data-sloton every data table part for styling. -
Performance. Cells are memoized, so selection and filter changes skip unchanged cells. A fixed table layout stops columns shifting as data loads.
-
Fixes. Header menu flicker, multi-sort, row highlights, and action bar actions now scoped to the visible selection.
Data Grid
Breaking changes
-
Cells are identified by row id, not row index.
CellPositionis now{ rowId, columnId }. Pass a stablegetRowIdso ids survive sorting and filtering. This changes:onRowAddreturns{ rowId, columnId }to focus a cell in the new row.onRowsDeletereceives(rows, rowIds)instead of(rows, rowIndices).onFilesUploadandonFilesDeletereceiverowIdinstead ofrowIndex.autoFocustakes{ rowId, columnId }.- Custom cells receive
rowIdandrowHeightinDataGridCellProps. Import the type from@/lib/data-grid-types.
const onRowAdd = React.useCallback(() => { const newRow = { id: generateId() }; setData((prev) => [...prev, newRow]); return { rowId: newRow.id, columnId: "name" }; }, []); -
Row height is table state. The
rowHeightoption is removed fromuseDataGrid. Set it withinitialState: { rowHeight: "tall" }, control it withstate.rowHeightandonRowHeightChange, or calltable.setRowHeight. -
onRowAddcan't returnundefined. Return a cell position, ornullto skip focusing.
New
- Cell editing feature. Editing is a table feature. Turn it off per grid or per column with
enableCellEditing: false, and read it withcell.getCanEdit(),cell.getIsEditing(), andtable.setEditingCell. onPaste. Called with the applied{ rowId, columnId, value }updates after a paste, so you can persist them.enableSingleCellSelection. A single click selects the clicked cell.- Grid-style keyboard navigation. Tab and arrow keys move between cells the way a spreadsheet does.
- Fixes. The multi-select cell search input.
Migrating
- Reinstall the components you use. Replace
data-table-sort-listanddata-table-filter-listwithdata-table-sort-menuanddata-table-filter-menu. - Rename imports and types using the tables above.
- Remove
enableAdvancedFilterandqueryKeys.filters. - If you parse filters on the server, switch to
getDataTableSearchParamsandgetDataTableQuery. - For Data Grid, replace row indices with row ids in your callbacks and move
rowHeightintoinitialState.