Skip to content

API reference

The full DataTable surface: props, the twelve two-way model slices, change events, the imperative handle, and the slots. For the <Column> attribute reference see Columns; for the per-framework consumption code see the usage page.

Props

The full prop surface. The twelve model: true slices (the Two-way column — data plus the eleven state slices) are each an independent, optional two-way r-model with an uncontrolled fallback; with multiple model props the Angular output emits no ControlValueAccessor (the multi-model condition disables it — the per-prop valueChange outputs still drive each two-way binding).

NameTypeDefaultTwo-way (model)RequiredDescription
dataArrayThe row data — model: true, so a committed cell/row edit writes a fresh array back through r-model:data (uncontrolled fallback dataDefault). A stable reference per Rozie's setup-once model — fed directly into table-core (never map/cloned in the watcher).
columnsArray[]Config-array column fallback (lower precedence than <Column> children). Each entry: { id?, field, header?, sortable?, filterable?, pinned?, width? }. Columns may come from this array, from <Column> children, or both (id-keyed last-write-wins union).
selectionModeString"none"Row-selection mode: 'none' | 'single' | 'multiple'. 'multiple' auto-injects a leading checkbox column with a select-all header.
sortingArray[]SortingState[{ id, desc }]. Uncontrolled fallback when unbound. Two-way: writes funnel a fresh value through the sort-change event regardless of binding.
globalFilterString''The global search string — narrows all columns. Feeds getFilteredRowModel(). Surfaces through filter-change. Two-way: fires filter-change regardless of binding.
columnFiltersArray[]ColumnFiltersState[{ id, value }] per-column narrowing (gated by each column's filterable). Two-way: whole-array replace on write, fires filter-change.
paginationObject{…}{ pageIndex, pageSize }. Defaults to { pageIndex: 0, pageSize: 10 }; feeds the prev/next + page-size chrome (and getPaginationRowModel()). Two-way: funnels a fresh object through page-change.
manualBooleanfalseServer-side hook: sets manualPagination / manualFiltering / manualSorting so table-core trusts the consumer-supplied rows and only emits the change events (the consumer fetches each page).
rowCountNumbernullTotal server-side row count for manual pagination; lets the table compute page count when it doesn't hold the full dataset.
pageCountNumbernullExplicit total page count for manual pagination; overrides rowCount-derived count.
expandableBooleanfalseOpt-in expandable rows. When true, a leading chevron expander column auto-injects (after the select column) and getExpandedRowModel activates; default false is byte-identical-off. Every row can expand to reveal a #detail panel unless getSubRows is supplied (then only rows with children expand). Bind :expandable="true" (a bare attr only coerces on Vue+Lit).
expandedanynullExpandedState{ [rowId]: true }, or the true literal after expandAll (declared type: [Object, Boolean]). Multi-expand (multiple rows open at once). Surfaces through expand-change; uncontrolled fallback ($data.expandedDefault) when unbound — the default is null so the uncontrolled fallback AND the grouping auto-expand default are reachable (a non-null default would short-circuit them). When grouping is active and expanded is untouched, group subtrees auto-expand.
getSubRowsFunctionnullTable-level child-row accessor (originalRow, index) => TData[] | undefined that drives nested sub-rows. When supplied (with expandable), table-core flattens the hierarchy and the expand seam reveals depth-indented child rows. Null → the #detail scoped slot is the expand mode.
groupableBooleanfalseOpt-in gate for the headless #groupBar host region. Default false is byte-identical-off. getGroupedRowModel is wired unconditionally (inert when grouping is empty), so grouping is driven by the grouping model; this flag only gates the consumer-facing group-bar surface (the component ships no built-in drag UI).
groupingArraynullGroupingState — an ordered string[] of column ids (multi-column → nested groups, e.g. ['region','category']). An empty/unbound list is ungrouped (byte-identical-off). Group-header rows are collapsible (they ride the expand model). Surfaces through group-change; uncontrolled fallback ($data.groupingDefault, default []) when unbound — the default is null (mirroring expanded) so the uncontrolled fallback is reachable and the grouping auto-expand default can activate when a consumer applies grouping without binding r-model:grouping (a non-null [] default would short-circuit it). All reads are null-guarded, so table-core still receives an array.
rowSelectionObject{}RowSelectionState{ [rowId]: true }. Checkbox-only toggle (the row body does not select). Driven by the selectionMode chrome. Two-way: fires selection-change regardless of binding.
columnVisibilityObject{}VisibilityState{ [colId]: boolean }. Hidden columns drop automatically from header + body. Two-way: funnels a fresh object through visibility-change.
columnSizingObject{}ColumnSizingState{ [colId]: number }. Driven live by the pointer-drag resize handle (columnResizeMode: 'onChange'). Two-way: fires resize-change.
columnOrderArray[]ColumnOrderStatestring[]. A fresh order array on reorder (never an in-place splice). Two-way: fires reorder-change.
columnPinningObject{…}ColumnPinningState{ left: string[], right: string[] }. Pinned columns get position: sticky + computed offsets. Defaults to { left: [], right: [] }. Two-way: fires pin-change.
stickyHeaderBooleanfalsePure-CSS sticky header: the <thead> sticks to the top of the scroll container.
interactionModeString"table"'table' (default, row-oriented, byte-behaviorally identical to a plain accessible table) | 'grid' (GA since Phase 63) — lights up the full WAI-ARIA grid interaction mode: role="grid", a roving single tab-stop, 2-D APG arrow-key cell navigation, range selection, and clipboard support.
singleClickEditBooleanfalseGrid mode only. When true, a plain click on an editable cell opens its editor immediately (single-click-to-edit) instead of just activating the cell. Default false keeps click-to-activate (double-click opens the editor). Shift+click (range selection) and clicks on non-editable cells are unaffected.
undoableBooleanfalseGrid mode. When true, every committed data mutation (cell/row edit, paste, fill, cut, clear) becomes one undo step: Ctrl/Cmd+Z undoes, Ctrl/Cmd+Y or Ctrl/Cmd+Shift+Z redoes. Default false records no history and Ctrl+Z/Y are inert.
undoLimitNumber100The maximum number of undo steps retained (oldest evicted past this depth). Only consulted when undoable is true.
virtualBooleanfalseOpt-in vertical row windowing. When true, only the visible slice of rows renders inside a bounded rdt-scroll container (with leading/trailing spacer rows preserving total scroll height), windowing over the full filtered + sorted (pre-pagination) model and suppressing the client pagination chrome. Default false is byte-identical to a non-virtual table.
estimateRowHeightNumber40Estimated row height (px) seeding the windowing engine before measureElement refines actual heights. Only consulted when virtual is on.
maxHeightString''A CSS length string bounding the rdt-scroll container when virtual is on (e.g. '400px'). Mirrored to the --rozie-data-table-max-height custom property; the prop wins, the token is the fallback.

For the per-column <Column> attributes (field / header / sortable / filterable / pinned / width / groupable / aggregationFn / editable / editor / editorOptions / validate), see the Columns reference.

Models (the twelve two-way slices)

Each slice is an independent, optional two-way r-model with its own uncontrolled fallback and its own change event (which fires regardless of whether the slice is bound). All twelve state transitions are funneled through table-core; the table always writes a fresh value (never an in-place mutation, which would be silently dropped on React/Solid/Angular/Lit). The first slice is data itself — a committed cell or row edit writes a fresh data array back; the remaining eleven are the table-state slices.

Model (r-model:)ShapeChange eventDescription
dataTData[]data-changeThe row data. Two-way so committed cell/row edits flow back (uncontrolled fallback dataDefault). Always written as a fresh array, never mutated in place.
sorting[{ id, desc }]sort-changeThe sort state (header click; shift-click adds a secondary sort).
globalFilterstringfilter-changeThe global search string — narrows all columns.
columnFilters[{ id, value }]filter-changePer-column filter values (gated by each column's filterable).
pagination{ pageIndex, pageSize }page-changeThe current page index + size.
rowSelection{ [rowId]: true }selection-changeThe selected-row set (checkbox-only).
expanded{ [rowId]: true } | trueexpand-changeThe expanded-row set (multi-expand; the true literal = all rows expanded).
groupingstring[]group-changeThe ordered grouping key list (multi-column → nested groups). [] is ungrouped.
columnVisibility{ [colId]: boolean }visibility-changePer-column shown/hidden state.
columnSizing{ [colId]: number }resize-changePer-column widths (live during a resize drag).
columnOrderstring[]reorder-changeThe full column order.
columnPinning{ left: string[], right: string[] }pin-changePer-side pinned-column ids.

Events

Every change event fires regardless of whether the matching r-model slice is bound (the uncontrolled-fallback contract), so you can observe transitions without two-way binding. The payload is the fresh slice value (filter-change carries { globalFilter } or { columnFilters } depending on which filter changed).

EventDescription
sort-changeFired when the sort state changes (header click / shift-click multi-sort / a sortColumn/clearSorting call). Payload: the fresh SortingState [{ id, desc }].
filter-changeFired when a filter changes. Payload { globalFilter } for the global search box or { columnFilters } for a per-column filter — both surface through this one event.
page-changeFired when pagination changes (prev/next, a page-size change, or a setPage/setRowsPerPage call). Payload: the fresh { pageIndex, pageSize }.
selection-changeFired when the row selection changes (a row/select-all checkbox toggle or a toggleAllRows/clearSelection call). Payload: the fresh RowSelectionState.
expand-changeFired when the expanded-row set changes (an expander chevron toggle — click / Enter / Space — or a toggleRowExpanded/expandAll/collapseAll call). Fires exactly once per change. Payload: the fresh ExpandedState ({ [rowId]: true }, or the true literal after expandAll — pass through verbatim, never Object.keys without a === true guard). Named expand-change (not expanded-change) so it does not collide with the expanded model's onExpandedChange callback on the React/Solid Props interface.
group-changeFired when the grouping changes (a #groupBar apply/clear, mutating the grouping model, or an applyGrouping/clearGrouping call). Fires exactly once per change. Payload: the fresh GroupingState — an ordered string[] of column ids, or [] when cleared. Named group-change (not grouping-change) so it does not collide with the grouping model's onGroupingChange callback on the React/Solid Props interface.
visibility-changeFired when a column is shown/hidden (the column-toggle menu or a toggleColumnVisibility call). Payload: the fresh VisibilityState.
resize-changeFired live during a column resize drag (columnResizeMode: 'onChange'). Payload: the fresh ColumnSizingState.
reorder-changeFired when the column order changes (an applyColumnOrder call or a header reorder). Payload: the fresh ColumnOrderState.
pin-changeFired when a column is pinned/unpinned (the per-header pin buttons or a pinColumn call). Payload: the fresh ColumnPinningState.
activecell-changeGrid mode only. Fired when the active cell moves (arrow-key navigation, a click-to-activate, or a focusCell call) — but not on a clamped no-op edge move. Payload: { rowIndex, colIndex } (integer position over the visible model). See Grid interaction mode.
cell-edit-commitFired when an editable cell commits a new, validated value (Enter, a blur-commit, or a commitEditing call). Payload: { rowId, columnId, oldValue, newValue }. Not fired on cancel (Escape) or a validation failure. See Editing.
row-edit-commitFired when a full-row edit commits all its changes at once (Enter in row-edit mode or an editRow save). Payload: { rowId, changes } where changes is [{ columnId, oldValue, newValue }] for the columns whose value actually changed. Not fired on Escape or a validation failure.
range-changeGrid mode only. Fired when the rectangular cell-range selection changes (extended via Shift+Arrow / Shift+Click). Payload: { anchor, focus } — each corner a { rowIndex, colIndex } index pair over the visible model, or { anchor: null, focus: null } when no range is set. One-way (this event + the getSelectedRange verb), never a model:true slice.
history-changeRequires undoable. Fired when undo/redo availability changes: always after an undo()/redo() call, and when a recorded edit flips canUndo/canRedo (edge-gated, so a streak of edits that leaves availability unchanged fires nothing). Payload: { canUndo: boolean, canRedo: boolean }. Drive undo/redo toolbar-button enablement from it.

Imperative handle

Declared once in the source via $expose; obtained through each framework's native ref mechanism. Each verb drives @tanstack/table-core so the matching change event fires.

MethodDescription
sortColumnToggle (or set) the sort for a column — sortColumn(colId, desc?). Fires sort-change.
clearSortingClear all sorting — clearSorting(). Resets to the unsorted core row model. Fires sort-change.
getColumnDefsReturn the resolved ColumnDef[] (the id-keyed LWW union of the :columns array and the <Column> children).
toggleAllRowsSelect or clear all (filtered) rows — toggleAllRows(value). Fires selection-change.
clearSelectionClear the row selection — clearSelection(). Fires selection-change with {}.
getSelectedRowsReturn the original row data for the selected rows — getSelectedRows()unknown[].
toggleRowExpandedToggle one row's expanded state — toggleRowExpanded(rowId) (the data id field or the table-core row id; both resolve). Multi-expand (does not collapse other rows). Fires expand-change.
expandAllOpen every expandable row — expandAll(). Fires expand-change (payload may be the true literal).
collapseAllCollapse every row — collapseAll(). Resets the expanded set to {}. Fires expand-change.
getExpandedRowsReturn the original row data for the currently-expanded rows — getExpandedRows()unknown[] (empty when nothing is expanded).
applyGroupingSet the full grouping — applyGrouping(cols) where cols is an ordered string[] of column ids (multi-column → nested groups). Fires group-change. (Named applyGrouping, not setGrouping, to avoid colliding with React's auto-generated grouping model setter.)
clearGroupingClear all grouping — clearGrouping(). Resets to the ungrouped (flat) row model. Fires group-change with [].
setPageGo to a 0-based page index — setPage(idx). Fires page-change.
setRowsPerPageSet the page size — setRowsPerPage(size). Fires page-change.
toggleColumnVisibilityShow/hide a column — toggleColumnVisibility(colId). Fires visibility-change.
applyColumnOrderSet the full column order — applyColumnOrder(order). Fires reorder-change. (Named applyColumnOrder, not setColumnOrder, to avoid colliding with React's auto-generated columnOrder model setter.)
resetColumnSizingReset all column widths to their defaults — resetColumnSizing(). Fires resize-change.
pinColumnPin a column to a side or unpin it — pinColumn(colId, side) where side is 'left' | 'right' | false. Fires pin-change.
getFacetedUniqueValuesReturn a column's cross-filtered distinct values — getFacetedUniqueValues(colId)unknown[] (keys only; occurrence counts are not exposed). Reflects rows passing all other active column filters. Empty array when unavailable. Inert (the faceted models stay off-path) until this verb or the #filter slot reads a facet. See Faceted filtering.
getFacetedMinMaxValuesReturn a numeric column's cross-filtered [min, max] range — getFacetedMinMaxValues(colId)[number, number] | null. null when unavailable or non-numeric. The read twin handed to the #filter slot for a numeric range slider.
editCellEditing. Open the editor on a cell — editCell(rowIndex, colIndex) (index-addressed over the visible model; args coerced to integers + clamped). No-op on a non-editable cell.
commitEditingEditing. Commit the open editor — commitEditing(). Runs the column validator; on success writes r-model:data and fires one cell-edit-commit; on a validation failure keeps the editor open. No-op when no cell is editing.
editRowEditing. Enter full-row edit on a body row — editRow(rowIndex) (the API twin of Shift+F2): every editable cell in the row opens at once; a later save commits the whole row in one r-model:data write + one row-edit-commit. No-op on a row with no editable columns.
getSelectedRangeReturn the current rectangular cell-range selection — getSelectedRange(){ anchor, focus } (each corner a { rowIndex, colIndex } index pair, or { anchor: null, focus: null }). One-way (this verb + range-change), never a model:true slice.
focusCellGrid mode. Move + DOM-focus the active cell by index — focusCell(rowIndex, colIndex) (coerced to integers and clamped to the visible model). Fires activecell-change.
getActiveCellGrid mode. Return the current active-cell position — getActiveCell(){ rowIndex, colIndex } (integers only; never a row object or DOM node).
clearActiveCellGrid mode. Reset the roving position to the entry cell (0,0) and exit interaction mode — clearActiveCell(). Does not emit (a reset, not a navigation).
getRowIndexRelativeToPageConvert an absolute display-order row index (the focusCell / getActiveCell / activecell-change space) to its page-relative index: getRowIndexRelativeToPage(absRow?); with no argument it converts the current active cell's row. In virtual mode (windowing replaces pagination) the absolute index is returned unchanged. Mirrors MUI's getRowIndexRelativeToVisibleRows.
cutGrid mode. Copy the current cell-range selection to the clipboard as TSV, then clear the source cells through the standard write funnel: cut() (the API twin of Ctrl+X). Read-only and validation-protected cells are left intact; each cleared cell fires cell-edit-commit; no-op while a header cell is active.
undoRequires undoable. Restore the data snapshot from before the most recent mutation (cell/row edit, paste, fill, cut): undo(). Replays through the standard write path, so it writes r-model:data, fires data-change, and fires history-change. The stack keeps up to undoLimit snapshots (oldest evicted). No-op when there is nothing to undo.
redoRequires undoable. Restore the most recently undone mutation: redo(). Same write path and events as undo. No-op when there is nothing to redo. A new recorded edit invalidates the redo stack (standard undo semantics).
canUndoReturn whether an undo snapshot is available: canUndo() returns a boolean (always false when undoable is off).
canRedoReturn whether a redo snapshot is available: canRedo() returns a boolean.
clearHistoryEmpty both the undo and redo stacks: clearHistory(). The table also clears its history automatically when the bound data is swapped externally (a new dataset should not inherit the old one's undo stack).

Slots

All slots live on the parent <DataTable> (a <Column> carries metadata only). The cell / colHeader slots are single renderers dispatched by columnId. On React/Solid these are render-prop props (renderCell / renderColHeader / cellSlot / colHeaderSlot) and on Lit the .cell / .colHeader properties — the one documented cross-framework divergence.

SlotParamsDescription
cellcolumnId, column, row, valueCustom cell renderer — switch on columnId to vary per column. A column it does not render shows the plain accessor value.
colHeadercolumnId, column, labelCustom header renderer — switch on columnId. Falls back to the plain header label.
selectAllchecked, indeterminate, toggleOverride the select-all header (only when selectionMode="multiple"). indeterminate is true on a partial selection.
selectCellrow, checked, toggleOverride the per-row select checkbox (only when selectionMode="multiple").
detailrowCustom expanded-row content rendered under an expanded row (only when expandable and no getSubRows). The React render-prop edge (documented divergence).
groupBargrouping, groupableColumns, applyGrouping, clearGroupingHeadless group-bar (only when groupable). Receives the ordered grouping array, the groupableColumns ([{ id, label }]), and the applyGrouping/clearGrouping helpers so a consumer builds any bar/drag UI. The default render is a non-interactive styled-token reflection (empty when ungrouped); the component ships no drag affordance. The React render-prop edge (documented divergence).
filtercolumnId, uniqueValues, minMax, setFilterHeadless faceted-filter UI (only when a column is filterable). uniqueValues is the cross-filtered distinct values (unknown[], keys only); minMax is the cross-filtered numeric [min, max] (or null); setFilter(columnId, value) applies a column filter through the same funnel the built-in input uses (value of null/'' clears it). Build a checkbox list / range slider and call setFilter (or drive columnFilters directly) — the slot fires no event. See Faceted filtering. The React render-prop edge (documented divergence). Pair it with the drop-in filter components.
editorcolumnId, column, row, value, commit, cancelCustom cell editor (a column with editor="custom", or to override the built-in editor). value is the current draft; commit(newValue) validates + commits (fires cell-edit-commit); cancel() closes without saving; column/row are opaque passthroughs. Pair it with the drop-in editor components. The React render-prop edge (documented divergence).

See also

  • Columns — the <Column> attribute reference and cell/header rendering.
  • Theming — the --rozie-data-table-* token vocabulary.
  • Per-framework usage code — the idiomatic consumption snippet per target.

Pre-1.0 — APIs may change between minor versions.