Next Chapter Begins 🚀Read the full story

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 selectionKeys and onSelectionChange to drive the selection from outside state.

const [selectionKeys, setSelectionKeys] = React.useState({});
 
const dt = useDataTable({
    data,
    dataKey: 'id',
    selectionMode: 'multiple',
    selectionKeys,
    onSelectionChange: (e) => setSelectionKeys(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

useDataTableRow#

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.