Introducing PrimeReact v11-alpha 🎉Discover Now

useDataTable

Hook that manages tabular data with sort, filter, pagination, selection, expansion, editing, column/row reorder, resize, and tree-mode flattening.

CodeNameCategoryPrice
basic-demo

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, and multiSortMeta
  • 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 totalRecords and onLazyLoad
  • 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 — cell and row modes; editingKeys and editingCell can be controlled, save and cancel events fire as expected
  • Column reorder & resize — drag-and-drop reorder, fit or expand resize, 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.

ScopePartStates
datatablerootdata-loading, data-row-hover, data-highlight-on-select, data-selection-mode, data-size, data-striped-rows, data-show-gridlines
datatabletable-container
datatabletable
datatablerowdata-index, data-selected, data-drag-source, data-dragpoint-top, data-dragpoint-bottom
datatablesortdata-sorted, data-unsorted, data-sort-order (asc / desc)
datatablefilterdata-display (row / menu), data-active
datatablecell-editordata-row-index, data-field, data-row-key, data-editing
datatablerow-toggledata-tree-level, aria-expanded
datatablecolumn-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#

NameTypeDefault
dataRecord<string, unknown>[] | object[]—
An array of objects to display.
defaultValuestring—
Default sort value for uncontrolled mode.
valuestring—
Sort field value for controlled mode.
dataKeystring—
A property to uniquely identify each row in the data.
loadingbooleanfalse
Whether the data is currently being loaded.
lazybooleanfalse
Defines if data is loaded and interacted with in lazy manner.
totalRecordsnumber0
Number of total records, used in lazy mode.
scrollablebooleanfalse
When enabled, the table can be scrolled horizontally and/or vertically.
scrollHeightstring—
Height of the scroll viewport in fixed pixels or the "flex" keyword for a dynamic size.
sortFieldstringundefined
Property name to sort by in single-sort mode.
sortOrderSortOrderundefined
Sort order for single-sort mode.
defaultSortFieldstringundefined
Default sort field for uncontrolled single-sort mode.
defaultSortOrderSortOrderundefined
Default sort order for uncontrolled single-sort mode.
multiSortMetaSortMeta[]undefined
An array of SortMeta objects for multi-column sorting (controlled).
defaultMultiSortMetaSortMeta[]undefined
Initial multi-column sort metadata for uncontrolled mode.
onSortChange(event: DataTableSortEvent) => void—
Callback invoked when the sort state changes.
removableSortbooleanfalse
When true, clicking a sorted column header a third time removes the sort.
nullSortOrdernumber1
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.
metaKeySelectionbooleanfalse
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.
selectedKeysSelectionKeysundefined
The selection keys for controlled mode.
defaultSelectedKeysSelectionKeysundefined
The default selection keys for uncontrolled mode.
onSelectionChange(event: DataTableSelectionEvent) => voidundefined
Callback invoked when the selection changes.
pagenumberundefined
Current page number for controlled pagination (0-indexed).
defaultPagenumber0
Default page number for uncontrolled pagination (0-indexed).
rowsnumberundefined
Number of rows to display per page for controlled mode.
defaultRowsnumber10
Default number of rows to display per page for uncontrolled mode.
rowsPerPageOptionsnumber[]undefined
Array of integer values to display inside the rows per page dropdown.
paginatorbooleanfalse
Whether pagination is enabled.
onPageChange(event: DataTablePageEvent) => void—
Callback invoked when the page or rows per page changes.
expandedKeysExpandedKeysundefined
The expanded keys map for controlled row expansion.
defaultExpandedKeysExpandedKeysundefined
The default expanded keys map for uncontrolled row expansion.
onExpandedChange(event: DataTableExpansionEvent) => void—
Callback invoked when the expanded keys change.
groupFieldstringundefined
The field name to group rows by.
reorderableColumnsbooleanfalse
When enabled, columns can be reordered using drag and drop.
onColumnReorder(event: DataTableColumnReorderEvent) => void—
Callback invoked when columns are reordered.
resizableColumnsbooleanfalse
When enabled, columns can be resized using drag and drop.
columnResizeModeColumnResizeMode'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.
editModeDataTableEditModeundefined
Defines the editing mode.
editingKeysEditingKeysundefined
The editing keys map for controlled row editing.
defaultEditingKeysEditingKeysundefined
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.
filtersDataTableFilterMetaundefined
Filter metadata for controlled mode.
defaultFiltersDataTableFilterMetaundefined
Default filter metadata for uncontrolled mode.
globalFilterstring—
Global filter value applied across globalFilterFields.
globalFilterFieldsstring[]undefined
An array of field names to search with globalFilter.
filterDelaynumber0
Delay in ms before applying filter (for debounce).
onFilter(event: DataTableFilterEvent) => void—
Callback invoked when filters change.
reorderableRowsbooleanfalse
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.
rowHoverbooleanfalse
When enabled, rows highlight on mouse hover via `data-row-hover` attribute on root.
highlightOnSelectbooleantrue
When enabled, selected rows are highlighted via `data-highlight-on-select` attribute on root.
treeModebooleanfalse
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.
stripedRowsbooleanfalse
When enabled, rows are rendered with alternating background colors.
showGridlinesbooleanfalse
When enabled, borders are displayed between cells.

useDataTableRow#

NameTypeDefault
itemRecord<string, unknown>—
The data object for this row.
indexnumber0
The index of the row in the data array.
contextuseDataTableInstance—
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.