Data Grid
Accessible, unstyled DataGrid (WAI-ARIA APG grid / treegrid): typed column definitions, multi-column sorting, filtering hooks, column resize, reorder, pinning and visibility, row selection, cell navigation and editing, tree rows, and optional row virtualization.
| Package | @vizejs/ui/data-grid |
| Maturity | stable |
| Own the source | vize lib pull data-grid |
| Requires | context, controllable-state, id, virtualizer |
| Aliases | data table, spreadsheet grid, tree grid, editable table |
| Covers | WAI-ARIA grid pattern, WAI-ARIA treegrid pattern, TanStack Table, AG Grid, React Aria Table |
Usage
import { DataGrid, DataGridCell, DataGridCellEditor, DataGridColumnHeader, DataGridResizeHandle, DataGridRow } from "@vizejs/ui/data-grid";
Or copy the source into your project with vize lib pull data-grid (see Source Distribution).
API
DataGrid
Source: src/families/data/data-grid/data-grid.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rows (required) |
readonly Row[] |
undefined |
Source rows. Nested rows come from getSubRows. |
columns (required) |
readonly Column[] |
undefined |
Column definitions, usually built with createColumnHelper<Row>(). |
id |
string | null |
undefined |
Consumer-owned grid id. null and undefined select a deterministic fallback. |
getRowId |
(row: Row, index: number, parentId: string | null) => string |
undefined |
Stable row id; defaults to the index path ("3", "3.1"). |
getSubRows |
(row: Row) => readonly Row[] | null | undefined |
undefined |
Sub rows; providing it renders role="treegrid". |
filterRow |
(row: Row) => boolean |
undefined |
Extra row predicate combined with column and global filters. |
selectionMode |
DataGridSelectionMode |
"none" |
Row selection policy. |
pageSize |
number |
10 |
Rows moved by PageUp and PageDown. |
dir |
"ltr" | "rtl" |
"ltr" |
Reading direction for horizontal arrow keys. |
virtualize |
boolean |
false |
Render only the rows in view (the root element is the scroll container). |
rowHeight |
number |
36 |
Fixed row height in CSS pixels used by virtualization. |
overscan |
number |
4 |
Extra virtualized rows rendered above and below the viewport. |
initialViewportHeight |
number |
400 |
Viewport height assumed during SSR and before measurement. |
ariaLabel |
string |
undefined |
Accessible name of the grid. |
ariaLabelledby |
string |
undefined |
Ids that label the grid. |
defaultState |
Partial<DataGridState> |
undefined |
Initial uncontrolled state slices. |
sorting |
readonly DataGridSort[] |
undefined |
Controlled sort keys (v-model:sorting). |
globalFilter |
string |
undefined |
Controlled global filter text (v-model:global-filter). |
columnFilters |
Readonly<Record<string, unknown>> |
undefined |
Controlled per-column filter values (v-model:column-filters). |
columnVisibility |
Readonly<Record<string, boolean>> |
undefined |
Controlled column visibility (v-model:column-visibility). |
columnOrder |
readonly string[] |
undefined |
Controlled column order (v-model:column-order). |
columnPinning |
DataGridPinning |
undefined |
Controlled pinning (v-model:column-pinning). |
columnSizing |
Readonly<Record<string, number>> |
undefined |
Controlled widths (v-model:column-sizing). |
selection |
readonly string[] |
undefined |
Controlled selected row ids (v-model:selection). |
expanded |
readonly string[] |
undefined |
Controlled expanded row ids (v-model:expanded). |
Events
| Event | Payload | Description |
|---|---|---|
update:sorting |
[value: readonly DataGridSort[]] |
Fired when sorting changes. |
update:globalFilter |
[value: string] |
Fired when the global filter changes. |
update:columnFilters |
[value: Readonly<Record<string, unknown>>] |
Fired when a column filter changes. |
update:columnVisibility |
[value: Readonly<Record<string, boolean>>] |
Fired when column visibility changes. |
update:columnOrder |
[value: readonly string[]] |
Fired when the column order changes. |
update:columnPinning |
[value: DataGridPinning] |
Fired when pinning changes. |
update:columnSizing |
[value: Readonly<Record<string, number>>] |
Fired when a column is resized. |
update:selection |
[value: readonly string[]] |
Fired when the row selection changes. |
update:expanded |
[value: readonly string[]] |
Fired when rows expand or collapse. |
cell-edit |
[event: DataGridCellEditEvent<Row>] |
Fired when a cell edit commits; update rows to apply it. |
Slots
| Slot | Slot props | Description |
|---|---|---|
header |
DataGridHeaderSlotProps<Row, Column> |
Column header contents. Defaults to the column's header or id. |
cell |
DataGridCellSlotProps<Row, Column> |
Cell contents, discriminated by columnId. Defaults to the formatted value. |
editor |
DataGridEditorSlotProps<Row> |
Editor for the cell being edited. Defaults to a text input. |
empty |
— | Rendered in the body when no rows remain after filtering. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
controller |
— | |
focusCell |
— | |
scrollToRow |
— |
DataGridCell
Source: src/families/data/data-grid/data-grid-cell.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rowId (required) |
string |
undefined |
Row id this cell belongs to. |
columnId (required) |
string |
undefined |
Column id this cell belongs to. |
colIndex (required) |
number |
undefined |
One-based aria-colindex. |
width |
number |
150 |
Column width in CSS pixels, published as --vize-ui-data-grid-column-width. |
pin |
DataGridPinSide | null |
null |
Pin side, published as data-pin. |
pinOffset |
number |
0 |
Sticky inset from the pinned edge, published as --vize-ui-data-grid-pin-offset. |
align |
DataGridAlign |
"start" |
Logical alignment hook. |
editable |
boolean |
false |
Whether the cell can be edited (Enter/F2 or double click). |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
— | Cell contents rendered by the grid root. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
DataGridCellEditor
Source: src/families/data/data-grid/data-grid-cell-editor.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label |
string |
undefined |
Accessible name of the editor input. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
DataGridColumnHeader
Source: src/families/data/data-grid/data-grid-column-header.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
columnId (required) |
string |
undefined |
Column id. |
colIndex (required) |
number |
undefined |
One-based aria-colindex. |
label |
string |
undefined |
Header text, also the resize handle's accessible name. |
width |
number |
150 |
Column width in CSS pixels. |
minWidth |
number |
40 |
Minimum width announced by the resize handle. |
maxWidth |
number |
Infinity |
Maximum width announced by the resize handle. |
pin |
DataGridPinSide | null |
null |
Pin side. |
pinOffset |
number |
0 |
Sticky inset from the pinned edge. |
align |
DataGridAlign |
"start" |
Logical alignment hook. |
sortable |
boolean |
false |
Whether clicking, Enter, or Space toggles sorting (Shift adds a sort key). |
sortDirection |
DataGridSortDirection | null |
null |
Current sort direction. |
sortIndex |
number | null |
null |
One-based priority among sort keys. |
resizable |
boolean |
false |
Render a pointer and keyboard resize handle. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
— | Header contents rendered by the grid root. |
DataGridResizeHandle
Source: src/families/data/data-grid/data-grid-resize-handle.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
columnId (required) |
string |
undefined |
Column id resized by this handle. |
label |
string |
undefined |
Column label used in the handle's accessible name. |
width |
number |
150 |
Current width, published as aria-valuenow. |
minWidth |
number |
40 |
Minimum width, published as aria-valuemin. |
maxWidth |
number |
Infinity |
Maximum width, published as aria-valuemax when finite. |
step |
number |
10 |
Pixels per Arrow key press. |
DataGridRow
Source: src/families/data/data-grid/data-grid-row.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rowId (required) |
string |
undefined |
Row id. |
rowIndex (required) |
number |
undefined |
One-based aria-rowindex (the header row is 1). |
selected |
boolean |
false |
Whether the row is selected. |
depth |
number |
0 |
Zero-based tree depth, published as aria-level in tree grids. |
expandable |
boolean |
false |
Whether the row has sub rows. |
expanded |
boolean |
false |
Whether the sub rows are shown. |
start |
number | null |
null |
Virtualized offset in CSS pixels, published as --vize-ui-data-grid-row-start. null means the row is laid out in normal flow. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
— | Row cells rendered by the grid root. |
Behavior
@vizejs/ui/data-grid implements the WAI-ARIA APG data grid
and treegrid patterns. data-grid.vue is the generic
root (Row, Column); data-grid-row.vue, data-grid-cell.vue, data-grid-column-header.vue,
data-grid-resize-handle.vue, and data-grid-cell-editor.vue are value-erased parts. The headless model
(useDataGrid) and pure row pipeline are proven in data-grid-model.test.ts; mounted behavior in
data-grid.test.ts; SSR in data-grid-ssr.test.ts; column inference in data-grid.types.test-d.ts.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| G1 | any | render | role="grid", aria-rowcount/aria-colcount, header columnheaders with aria-sort, rows with aria-rowindex, typed cell slots |
renders APG grid roles, counts, indices, and typed cell slots |
| G2 | sortable header | click / Shift+click | ascending → descending → off; Shift adds lower-priority keys; update:sorting fires |
header clicks sort ascending, descending, then off; Shift adds sort keys |
| G3 | sorted | null/undefined values |
missing values sort last in both directions | missing values sort last in both directions |
| G4 | filters | global, column filter, filterRow |
rows narrow; data-empty and the empty slot render when nothing matches |
global, column, and custom filters narrow rows and show the empty slot |
| G5 | focus in grid | arrows, Home/End, Ctrl+Home/End, Page keys | one roving tab stop moves across header and body cells, clamped at edges | arrow, Home/End, Ctrl+Home/End, and Page keys move a single roving tab stop |
| G6 | dir="rtl" |
ArrowLeft/ArrowRight | horizontal movement flips | rtl flips horizontal cell movement |
| G7 | header focus | Enter/Space, Ctrl+Shift+arrows, Alt+arrows | toggles sort, moves the column (focus follows), resizes by 10 px | header keys sort, reorder with Ctrl+Shift+arrows, and resize with Alt+arrows |
| G8 | resizable column | handle pointer drag / arrows | role="separator" with value range; width follows the pointer, clamps to minWidth/maxWidth, publishes width CSS variables |
resize handles are labelled separators driven by pointer and arrows within limits |
| G9 | layout state | visibility, order, pinning, sizing | hidden columns disappear, order applies, pinned columns group at each edge with sticky offsets and total width variables | visibility, order, and pinning props lay out columns with sticky offsets |
| G10 | selectionMode="multiple" |
click/Ctrl/Shift, Space, Shift+Arrow, Ctrl+A | replace, toggle, range, keyboard toggle/extend, select all; aria-selected and aria-multiselectable |
multiple selection: click replaces, Ctrl toggles, Shift extends, Space and Ctrl+A |
| G11 | single / none | click | single keeps one row; none publishes no aria-selected |
single selection keeps one row and none mode publishes no aria-selected |
| G12 | editable cell | Enter/F2/double click, typing, Enter/Tab/Escape | editor takes focus; validate errors keep it open with aria-invalid; parse converts; cell-edit fires; focus returns to the cell |
Enter or F2 edits, Enter commits through parse and validate, Escape cancels |
| G13 | read-only cell | Enter | nothing happens; aria-readonly="true" |
read-only cells never enter edit mode |
| G14 | getSubRows |
ArrowRight/ArrowLeft on the first cell | role="treegrid" with aria-level/aria-expanded; expand, collapse, move to parent; filters keep ancestors |
tree rows render a treegrid with levels; arrows expand, collapse, and climb |
| G15 | virtualize |
render / Ctrl+End | only a window of rows renders with absolute aria-rowindex and offset variables; focus scrolls the target row in |
virtualization renders a window with absolute row indices and scrolls to focus |
| G16 | custom layouts | import | parts are exported | exported parts stay importable for custom layouts |
| G17 | model | sort toggles | cycle and multi-key priority | toggleSorting cycles ascending, descending, off and keeps multi-key priority |
| G18 | model | dot paths | nullish segments short-circuit | readPath short-circuits nullish segments and ignores non-objects |
| G19 | model | tree pipeline | depth, parents, selection, expansion, stable sort within levels | buildRowModels flattens expanded trees with depth, parents, and stable sort |
| G20 | model | default compare | locale- and numeric-aware ordering | custom compare and default string comparison are locale and numeric aware |
| G21 | model, uncontrolled | state actions | every slice updates and reports through onStateChange; hideable/resizable/display-column rules hold |
uncontrolled slices update state and report every change |
| G22 | model, controlled | state actions | controlled slices only change through the owner | controlled slices only change through the owner |
| G23 | model | edit commit | parse errors surface, validation gates, rows are never mutated | edits parse, validate, report parse errors, and never mutate rows |
| G24 | SSR | two requests | byte-identical markup including default sorting and selection | renders byte-identical sorted, selected grid markup across SSR requests |
| G25 | SSR | hydrate plain/tree/virtual | no diagnostics; server root reused | hydrates plain, tree, and virtualized grids without diagnostics |
Extension hooks
| Hook | Values |
|---|---|
| parts | root, header, header-row, column-header, resize-handle, body, row, cell, cell-editor, empty |
| data | data-vize-ui, data-row-id, data-column-id, data-sort, data-sort-index, data-pin, data-align, data-active, data-editing, data-selected, data-depth, data-virtualized |
| CSS vars | --vize-ui-data-grid-column-width, --vize-ui-data-grid-pin-offset, --vize-ui-data-grid-total-width, --vize-ui-data-grid-body-height, --vize-ui-data-grid-row-start |