useDataTable
Hook that manages tabular data with sort, filter, pagination, selection, expansion, editing, column/row reorder, resize, and tree-mode flattening.
| Code | Name | Category | Price |
|---|
Usage#
import { useDataTable } from '@primereact/headless/datatable';const dt = useDataTable({ data, dataKey: 'id' });
const { state, rootProps, tableContainerProps, tableProps, getRowKey, sort, selection, pagination, expansion, filter, editing, columnReorder, columnResize, rowReorder, exportData, keyboard } = dt;useDataTable is a single composite hook: every feature (sort, filter, pagination, selection, expansion, editing, etc.) is composed inside it and exposed under a namespaced key. See Primitive for a component-based API.
Features#
- Sort — single, multi, and removable tri-state via
sortField,sortOrder, andmultiSortMeta - Filter — row and menu filters per column with multiple constraints and AND/OR operators, plus a global filter across selected fields
- Pagination — built-in slicing or lazy mode with
totalRecordsandonLazyLoad - Selection — single, multiple (checkbox or row), radio, Shift-range, Ctrl/Meta additive, and indeterminate parents in tree mode
- Expansion — flat row expanders or tree-mode flattening; rows are stamped with
_treeLevel,_treeHasChildren,_treePosInSet,_treeSetSize - Editing —
cellandrowmodes;editingKeysandeditingCellcan be controlled, save and cancel events fire as expected - Column reorder & resize — drag-and-drop reorder,
fitorexpandresize, with hooks for both indicators - Row reorder — drag-and-drop driven from a row handle
- Grouping — group metadata map keyed by row index for grouped headers and row spans
- Tree mode — the same API works on hierarchical data; sort, filter, pagination, and selection all operate tree-aware
- CSV export — export visible, selected, or all rows with custom fields and headers
- Keyboard navigation — arrows (with Shift to extend selection), Home / End, Arrow Left / Right to collapse / expand a tree row, Enter or Space to select, Enter on a row to open the first editable cell
Working with callbacks#
Controlled selection#
Pass selectedKeys and onSelectionChange to drive the selection from outside state.
const [selectedKeys, setSelectedKeys] = React.useState({});
const dt = useDataTable({
data,
dataKey: 'id',
selectionMode: 'multiple',
selectedKeys,
onSelectionChange: (e) => setSelectedKeys(e.value)
});Controlled sort#
Mix single and multi-sort by reading both sortField / sortOrder and multiSortMeta from state.
const [sortField, setSortField] = React.useState<string>();
const [sortOrder, setSortOrder] = React.useState<1 | -1 | 0>();
const dt = useDataTable({
data,
sortField,
sortOrder,
onSortChange: (e) => {
setSortField(e.field);
setSortOrder(e.order);
}
});Controlled filter#
Filters are a record of { value, matchMode, constraints?, operator? }. Both the per-column metadata and the global filter are debounceable via filterDelay.
const [filters, setFilters] = React.useState({
name: { value: null, matchMode: 'contains' },
price: { value: null, matchMode: 'gte' }
});
const [globalFilter, setGlobalFilter] = React.useState('');
const dt = useDataTable({
data,
filters,
onFilter: (e) => setFilters(e.filters),
globalFilter,
globalFilterFields: ['name', 'category'],
filterDelay: 200
});Lazy loading#
In lazy mode the consumer drives data fetching. The hook returns the data as-is and emits onLazyLoad whenever page, sort, or filter state changes.
const dt = useDataTable({
data,
lazy: true,
paginator: true,
totalRecords,
onLazyLoad: (e) => fetchPage(e)
});Tree mode#
Pass nodes with key, data, and optional children; flip treeMode. The hook flattens visible nodes (respecting expandedKeys), and stamps each row with _treeLevel, _treeHasChildren, _treePosInSet, _treeSetSize so the renderer can draw indentation, expanders, and ARIA aria-level / aria-posinset.
const dt = useDataTable({
data: nodes,
treeMode: true,
dataKey: 'key',
expandedKeys,
onExpandedChange: (e) => setExpandedKeys(e.value)
});Cell and row editing#
const dt = useDataTable({
data,
editMode: 'cell',
onCellEditComplete: (e) => {
// mutate `data` based on `e.rowData`, `e.field`, `e.newValue` — and call `e.preventDefault()` to keep the cell open
}
});Switch to editMode="row" and use onRowEditSave / onRowEditCancel for whole-row commits.
Per-row state with useDataTableRow#
For hooking up a single <tr> to selection / expansion / reorder / editing, pair useDataTable with useDataTableRow. It returns a single rowProps getter pre-wired with role, aria-rowindex, click / key handlers, drag handlers when reorder is active, plus tree-mode ARIA (aria-level, aria-expanded, aria-posinset, aria-setsize).
import { useDataTable, useDataTableRow } from '@primereact/headless/datatable';
const dt = useDataTable({ data, dataKey: 'id', selectionMode: 'multiple' });
function Row({ item, index }) {
const row = useDataTableRow({ item, index, context: dt });
return <tr {...row.rowProps}>{/* cells */}</tr>;
}Styling with data attributes#
Every part exposes its state through data-* attributes — drive your CSS from those instead of class toggles.
| Scope | Part | States |
|---|---|---|
datatable | root | data-loading, data-row-hover, data-highlight-on-select, data-selection-mode, data-size, data-striped-rows, data-show-gridlines |
datatable | table-container | |
datatable | table | |
datatable | row | data-index, data-selected, data-drag-source, data-dragpoint-top, data-dragpoint-bottom |
datatable | sort | data-sorted, data-unsorted, data-sort-order (asc / desc) |
datatable | filter | data-display (row / menu), data-active |
datatable | cell-editor | data-row-index, data-field, data-row-key, data-editing |
datatable | row-toggle | data-tree-level, aria-expanded |
datatable | column-resizer |
[data-scope='datatable'][data-part='row'][data-selected] {
background: var(--p-primary-50);
}
[data-scope='datatable'][data-part='sort'][data-sort-order='asc']::after {
content: '↑';
}
[data-scope='datatable'][data-part='cell-editor'][data-editing] {
outline: 2px solid var(--p-primary-color);
}
[data-scope='datatable'][data-part='row'][data-dragpoint-top] {
box-shadow: 0 -2px 0 var(--p-primary-color) inset;
}API#
useDataTable#
| Name | Type | Default |
|---|---|---|
data | Record<string, unknown>[] | object[] | — |
| An array of objects to display. | ||
defaultValue | string | — |
| Default sort value for uncontrolled mode. | ||
value | string | — |
| Sort field value for controlled mode. | ||
dataKey | string | — |
| A property to uniquely identify each row in the data. | ||
loading | boolean | false |
| Whether the data is currently being loaded. | ||
lazy | boolean | false |
| Defines if data is loaded and interacted with in lazy manner. | ||
totalRecords | number | 0 |
| Number of total records, used in lazy mode. | ||
scrollable | boolean | false |
| When enabled, the table can be scrolled horizontally and/or vertically. | ||
scrollHeight | string | — |
| Height of the scroll viewport in fixed pixels or the "flex" keyword for a dynamic size. | ||
sortField | string | undefined |
| Property name to sort by in single-sort mode. | ||
sortOrder | SortOrder | undefined |
| Sort order for single-sort mode. | ||
defaultSortField | string | undefined |
| Default sort field for uncontrolled single-sort mode. | ||
defaultSortOrder | SortOrder | undefined |
| Default sort order for uncontrolled single-sort mode. | ||
multiSortMeta | SortMeta[] | undefined |
| An array of SortMeta objects for multi-column sorting (controlled). | ||
defaultMultiSortMeta | SortMeta[] | undefined |
| Initial multi-column sort metadata for uncontrolled mode. | ||
onSortChange | (event: DataTableSortEvent) => void | — |
| Callback invoked when the sort state changes. | ||
removableSort | boolean | false |
| When true, clicking a sorted column header a third time removes the sort. | ||
nullSortOrder | number | 1 |
| Determines where null values are placed during sort. 1 = nulls last (default), -1 = nulls first. | ||
selectionMode | "multiple" | "single" | — |
| Defines the selection mode. 'single' allows one row, 'multiple' allows many. | ||
metaKeySelection | boolean | false |
| When true, row click selection requires Ctrl/Cmd key. Without it, click selects only that row. Shift+Click extends range. Only applies when selectionMode is set. | ||
selectedKeys | SelectionKeys | undefined |
| The selection keys for controlled mode. | ||
defaultSelectedKeys | SelectionKeys | undefined |
| The default selection keys for uncontrolled mode. | ||
onSelectionChange | (event: DataTableSelectionEvent) => void | undefined |
| Callback invoked when the selection changes. | ||
page | number | undefined |
| Current page number for controlled pagination (0-indexed). | ||
defaultPage | number | 0 |
| Default page number for uncontrolled pagination (0-indexed). | ||
rows | number | undefined |
| Number of rows to display per page for controlled mode. | ||
defaultRows | number | 10 |
| Default number of rows to display per page for uncontrolled mode. | ||
rowsPerPageOptions | number[] | undefined |
| Array of integer values to display inside the rows per page dropdown. | ||
paginator | boolean | false |
| Whether pagination is enabled. | ||
onPageChange | (event: DataTablePageEvent) => void | — |
| Callback invoked when the page or rows per page changes. | ||
expandedKeys | ExpandedKeys | undefined |
| The expanded keys map for controlled row expansion. | ||
defaultExpandedKeys | ExpandedKeys | undefined |
| The default expanded keys map for uncontrolled row expansion. | ||
onExpandedChange | (event: DataTableExpansionEvent) => void | — |
| Callback invoked when the expanded keys change. | ||
groupField | string | undefined |
| The field name to group rows by. | ||
reorderableColumns | boolean | false |
| When enabled, columns can be reordered using drag and drop. | ||
onColumnReorder | (event: DataTableColumnReorderEvent) => void | — |
| Callback invoked when columns are reordered. | ||
resizableColumns | boolean | false |
| When enabled, columns can be resized using drag and drop. | ||
columnResizeMode | ColumnResizeMode | 'fit' |
| Defines whether the overall table width should change on column resize. 'fit' mode keeps total width the same. 'expand' mode increases total width. | ||
onColumnResizeEnd | (event: DataTableColumnResizeEvent) => void | — |
| Callback invoked when a column resize ends. | ||
editMode | DataTableEditMode | undefined |
| Defines the editing mode. | ||
editingKeys | EditingKeys | undefined |
| The editing keys map for controlled row editing. | ||
defaultEditingKeys | EditingKeys | undefined |
| The default editing keys for uncontrolled row editing. | ||
onEditingKeysChange | (event: DataTableEditingEvent) => void | — |
| Callback invoked when the editing keys change. | ||
onRowEditInit | (event: DataTableRowEditEvent) => void | — |
| Callback invoked when a row edit is initiated. | ||
onRowEditSave | (event: DataTableRowEditEvent) => void | — |
| Callback invoked when a row edit is saved. | ||
onRowEditCancel | (event: DataTableRowEditEvent) => void | — |
| Callback invoked when a row edit is cancelled. | ||
onCellEditInit | (event: { originalEvent: SyntheticEvent; field: string; rowIndex: number }) => void | — |
| Callback invoked when a cell edit is initiated. | ||
onCellEditComplete | (event: DataTableCellEditEvent) => void | — |
| Callback invoked when a cell edit completes. | ||
onCellEditCancel | (event: DataTableCellEditEvent) => void | — |
| Callback invoked when a cell edit is cancelled. | ||
filters | DataTableFilterMeta | undefined |
| Filter metadata for controlled mode. | ||
defaultFilters | DataTableFilterMeta | undefined |
| Default filter metadata for uncontrolled mode. | ||
globalFilter | string | — |
| Global filter value applied across globalFilterFields. | ||
globalFilterFields | string[] | undefined |
| An array of field names to search with globalFilter. | ||
filterDelay | number | 0 |
| Delay in ms before applying filter (for debounce). | ||
onFilter | (event: DataTableFilterEvent) => void | — |
| Callback invoked when filters change. | ||
reorderableRows | boolean | false |
| When enabled, rows can be reordered using drag and drop. | ||
onRowReorder | (event: DataTableRowReorderEvent) => void | — |
| Callback invoked when rows are reordered. | ||
rowClassName | (data: Record<string, unknown>, options: { index: number; props: useDataTableProps }) => string | — |
| A function that returns a className for a given row. | ||
onLazyLoad | (event: DataTableLazyLoadEvent) => void | — |
| Callback invoked when lazy loading is triggered (page, sort, filter change in lazy mode). | ||
onRowClick | (event: DataTableRowMouseEvent) => void | — |
| Callback fired when a row is clicked. Does NOT fire when the click originates on interactive content (buttons, inputs, checkboxes, radios) — those bubble their own semantics. | ||
onRowDoubleClick | (event: DataTableRowMouseEvent) => void | — |
| Callback fired on row double-click. | ||
onRowContextMenu | (event: DataTableRowMouseEvent) => void | — |
| Callback fired on row right-click (contextmenu). Does not prevent the default menu — call `event.originalEvent.preventDefault()` in the handler if you want to. | ||
rowHover | boolean | false |
| When enabled, rows highlight on mouse hover via `data-row-hover` attribute on root. | ||
highlightOnSelect | boolean | true |
| When enabled, selected rows are highlighted via `data-highlight-on-select` attribute on root. | ||
treeMode | boolean | false |
| When enabled, data is treated as a tree structure with `key` , `data` , and `children` fields. Tree nodes are flattened internally and sort, filter, pagination operate on the tree structure. Pagination applies to root-level nodes only. | ||
size | "small" | "large" | undefined |
| Table size variant. Adjusts cell padding and font size via CSS. | ||
stripedRows | boolean | false |
| When enabled, rows are rendered with alternating background colors. | ||
showGridlines | boolean | false |
| When enabled, borders are displayed between cells. | ||
useDataTableRow#
| Name | Type | Default |
|---|---|---|
item | Record<string, unknown> | — |
| The data object for this row. | ||
index | number | 0 |
| The index of the row in the data array. | ||
context | useDataTableInstance | — |
| The parent datatable instance providing state and methods. | ||
Accessibility#
role="table" is applied for flat data and role="treegrid" when treeMode is enabled. Each row exposes aria-rowindex (1-based) and, in tree mode, aria-level, aria-expanded, aria-posinset, and aria-setsize. Sort triggers expose role="button", aria-sort, and a descriptive aria-label. Cell-edit wrappers tag their <td> with data-editable-cell so Tab traversal can find and focus the next editable cell. See Primitive for full WAI-ARIA compliance details.