When to Use
Use DataGrid when users must move through rows and columns with the keyboard or
select rows in an interactive table. Use a native table when people only need
to read data; native tables are simpler and already accessible. Use TreeGrid
when rows expand into a hierarchy. DataGrid does not provide sorting logic,
editing, resizing, filtering, or virtualization—it exposes the states needed
to connect those application features.
Features
The behavior Atom owns before your product adds appearance.
- Implements grid, rowgroup, row, columnheader, and gridcell semantics.
- Keeps focus on Root and identifies the active cell with
aria-activedescendant.
- Supports controlled active cell and none, single, or multiple row selection.
- Navigates in document order while skipping disabled cells.
- Supports row/column counts, looping, row wrapping, and row-click selection.
- Gives actionable column headers equivalent pointer and Enter activation.
- Mirrors horizontal arrows in RTL.
- Supports native table rendering plus
asChild and render composition.
Import
Anatomy
API Reference
All rendered parts accept the native props for their default table element and
support asChild and render.
Root
Renders a focusable table, owns active-cell navigation and row selection,
and provides the grid context.
value | string | string[] | null | - |
defaultValue | string | string[] | null | [] for multiple; otherwise null |
onValueChange | (value: DataGridSelectionValue) => void | - |
activeCell | { rowIndex: number; columnIndex: number } | null | - |
defaultActiveCell | { rowIndex: number; columnIndex: number } | null | null |
onActiveCellChange | (cell) => void | - |
selectionMode | "none" | "single" | "multiple" | "none" |
dir | "ltr" | "rtl" | Direction context |
disabled | boolean | false |
readOnly | boolean | false |
loop | boolean | false |
wrapRows | boolean | false |
rowCount | number | - |
columnCount | number | - |
selectOnRowClick | boolean | false |
asChild | boolean | false |
render | RenderProp | - |
Counts are truncated to positive integers. Missing or invalid counts are
announced as -1, meaning the total is unknown.
role | "grid" |
aria-activedescendant | Generated active Cell ID |
aria-colcount | Normalized count or -1 |
aria-rowcount | Normalized count or -1 |
aria-disabled | "true" when disabled |
aria-readonly | "true" when read only |
aria-multiselectable | "true" in multiple mode |
[data-slot] | "data-grid" |
[data-active] | Present when an active registered Cell exists |
[data-focused] | Present while focus is within Root |
[data-disabled] | Present when disabled |
[data-readonly] | Present when read only |
[data-column-count] | Normalized supplied count |
[data-row-count] | Normalized supplied count |
[data-selection-mode] | "single" | "multiple" |
Caption
Renders the native caption that names or summarizes the grid for table users.
asChild | boolean | false |
render | RenderProp | - |
[data-slot] | "data-grid-caption" |
Renders a thead row group for heading rows.
asChild | boolean | false |
render | RenderProp | - |
[data-slot] | "data-grid-header" |
Row
Renders a tr, supplies row metadata to its cells, and optionally participates
in selection. rowIndex is one-based; zero-based index is converted to one-based.
value | string | - |
rowIndex | number | - |
index | number | - |
selectable | boolean | true |
disabled | boolean | false |
asChild | boolean | false |
render | RenderProp | - |
Rows need value to be selectable. selectable={false} keeps an identified
header, footer, or summary row out of selection without disabling its cells.
role | "row" |
aria-rowindex | Normalized one-based row index |
aria-selected | Row selection state when selection is enabled |
aria-disabled | "true" when Row or Root is disabled |
[data-slot] | "data-grid-row" |
[data-selectable] | Present for a selectable valued row |
[data-selection-disabled] | Present when the row opts out of enabled selection |
[data-row-index] | Normalized index |
[data-value] | Row value |
[data-selected] | Present when selected |
[data-disabled] | Present when disabled |
ColumnHeader
Renders a th registered as a navigable cell. It exposes sort state but does
not change sorting when clicked.
columnIndex | number | - |
index | number | - |
disabled | boolean | false |
sortDirection | "ascending" | "descending" | "none" | "other" | - |
onAction | () => void | - |
scope | native th scope | "col" |
asChild | boolean | false |
render | RenderProp | - |
role | "columnheader" |
aria-colindex | Normalized one-based column index |
aria-sort | Value from sortDirection |
aria-selected | Parent Row selection state when enabled |
aria-disabled | "true" when explicitly, row, or grid disabled |
[data-slot] | "data-grid-column-header" |
[data-column-index] | Normalized index |
[data-sort] | Sort direction when supplied |
[data-actionable] | Present when an enabled indexed header has onAction |
[data-active] | Present when active and Root is focused |
[data-selected] | Present when its Row is selected |
[data-disabled] | Present when explicitly, row, or grid disabled |
Body
Renders a tbody row group for the main data rows.
asChild | boolean | false |
render | RenderProp | - |
[data-slot] | "data-grid-body" |
Cell
Renders a registered td. Clicking an enabled indexed Cell makes it active
and focuses Root; missing indexes make it non-navigable without announcing it
as disabled.
columnIndex | number | - |
index | number | - |
disabled | boolean | false |
asChild | boolean | false |
render | RenderProp | - |
role | "gridcell" |
aria-colindex | Normalized one-based column index |
aria-selected | Parent Row selection state when enabled |
aria-disabled | "true" when explicitly, row, or grid disabled |
[data-slot] | "data-grid-cell" |
[data-column-index] | Normalized index |
[data-active] | Present when active and Root is focused |
[data-selected] | Present when its Row is selected |
[data-disabled] | Present when explicitly, row, or grid disabled |
Renders a tfoot row group for totals and summary rows.
asChild | boolean | false |
render | RenderProp | - |
[data-slot] | "data-grid-footer" |
Advanced custom parts can use the public useDataGridContext and
useDataGridRowContext hooks. The row hook returns null outside Row; the main
hook must be used inside Root. Prefer the namespaced parts for standard grids.
Examples
Selectable Project Grid
Accessibility
DataGrid follows the
WAI-ARIA Grid pattern.
Provide an accessible name with Caption, aria-label, or aria-labelledby.
Every navigable row and cell needs a valid one-based index. Root receives DOM
focus while aria-activedescendant identifies the active Cell.
ArrowRight / ArrowLeft | Moves within a row; directions mirror in RTL. |
ArrowDown / ArrowUp | Moves by row, preferring the same column and skipping disabled cells. |
Home / End | Moves to the first or last enabled cell in the current row. |
Ctrl+Home / Cmd+Home | Moves to the first enabled grid cell. |
Ctrl+End / Cmd+End | Moves to the last enabled grid cell. |
Enter / Space | Toggles/selects the active Cell's Row when selection is enabled. |
Enter | Calls onAction when the active cell is an actionable ColumnHeader. |
readOnly prevents selection changes but still permits navigation. disabled
prevents Root keyboard handling. Rows with selectable={false} ignore row
selection from click, Enter, and Space.
ColumnHeader.onAction is the pointer/keyboard activation boundary for
application-controlled sorting or another header command. Atom calls it after
an enabled indexed header is clicked and when Enter is pressed while that
header is active. Atom does not change sortDirection or reorder data.
Consumer onClick remains a native pointer-only escape hatch and may prevent
Atom's composed pointer behavior with event.preventDefault().
Changelog
Unreleased
0.24.0
- Added source-led Agent Knowledge for grid selection, indexed semantics,
active-descendant focus, row selection, header actions, and virtualization
boundaries.
0.17.0
- Added
ColumnHeader.onAction with data-actionable and equivalent pointer
and active-header Enter dispatch for application-controlled sorting.
0.2.0
- Fixed vertical keyboard navigation to preserve the active column while
skipping disabled cells in intervening rows.
- Added
DataGrid.Row selectable behavior with data-selectable and
data-selection-disabled attributes so rows can opt out of selection without
being disabled.
- Added
dir and Direction.Provider support so horizontal cell navigation
mirrors in RTL.
0.1.0