Accessibility

The keyboard navigation model, ARIA attributes, and live announcements built into DataGridComponent.

Last updated August 24, 2026

DataGridComponent implements the WAI-ARIA grid pattern: role="grid", a single roving tab stop, and a live region for changes no visual cue alone would reach a screen reader with. A grid with an active groupBy switches to role="treegrid" — the WAI-ARIA pattern for expandable, collapsible rows — instead; an ungrouped grid is unaffected either way.

Keyboard navigation#

One cell holds the grid's tab stop at a time — Tab reaches the grid once, and the arrow keys move within it from there.

KeyMoves to
ArrowUp / ArrowDown / ArrowLeft / ArrowRightThe adjacent cell in that direction.
Home / EndThe first / last cell of the current row.
Ctrl+Home / Ctrl+EndThe first / last cell of the whole grid.
PageUp / PageDownUp or down by however many rows fit the viewport.

Alt+ArrowLeft/ArrowRight and Ctrl+ ArrowLeft/ArrowRight are reserved for column resize and column reorder respectively, not plain navigation — a focused header claims them first. See column resizing and column reordering for what they do there. Alt+ArrowDown is likewise reserved on a groupable header, for adding or removing it from the group-by stack — see row grouping.

A group header renders as one cell spanning every column, so it holds the tab stop as a single unit rather than N per-column cells: ArrowLeft/ArrowRight are a no-op on it, and Space/Enter expand or collapse it in place of selecting anything.

Under groupAggregateDisplay: "row" (see aggregate functions), a group's own summary row is presentational rather than an addressable stop: ArrowUp/ArrowDown/PageUp/PageDown/Ctrl+End step straight over it, landing on the nearest row that isn't one. It still occupies a real position for aria-rowindex/aria-rowcount — see below — only the tab stop skips it.

The group-by bar sits outside the grid's own table and its single-roving-tab-stop model entirely — there's no shared position for a chip to keep in sync the way a cell has, so each chip (and its own remove button) is simply, always its own tab stop, in plain DOM order. Drag a chip to reorder the stack, or focus one and press

Ctrl+ArrowLeft / Ctrl+ArrowRight — the same modifier and semantics a column header's own reorder shortcut uses. A groupable header whose column sets groupByDraggable can also be dragged straight into the bar, landing at whichever position among the existing chips it's released.

ARIA attributes#

AttributeWhereDescription
aria-rowcountThe gridTotal rows, header included — one more than the row count of dataSource as filtered, sorted, and grouped, plus one per group's own summary row under groupAggregateDisplay: "row". The full dataset count, not just the current page's, once paginated is on.
aria-colcountThe gridTotal columns as currently sized and ordered.
aria-rowindexEvery row1-based; the header is row 1, so the first body row is row 2. Built from each row's absolute dataset position, not its page-relative one — see pagination.
aria-colindexEvery header and body cell1-based, following the current column order.
aria-multiselectableThe gridSet to true when selectable.rows or selectable.columns is "multiple". Omitted otherwise.
aria-selectedRow, column header, and cellReflects whether that row/column/cell is selected — but only when that dimension's selection is enabled at all. Omitted, not false, when it isn't: a display-only grid never claims to have an unselected anything.
aria-sortColumn headers"ascending" or "descending" once a sortable column has an active sort direction. Omitted — not "none" — for an unsorted or unsortable column.
aria-keyshortcutsColumn headers and body cellsLists whichever shortcuts that column or cell actually responds to (below), so they're discoverable rather than assumed.
aria-expandedGroup header rowsWhether that group is currently expanded or collapsed.
aria-levelGroup header rows1-based nesting depth — 1 for a top-level group, 2 for one nested inside it, and so on.
aria-posinset / aria-setsizeGroup header rowsThat group's 1-based position among, and count of, the group headers sharing its level and immediate parent.

A column header's aria-keyshortcuts names whichever of these apply, space-separated:

  • Control+ArrowLeft Control+ArrowRight when the column is reorderable.
  • Alt+ArrowLeft Alt+ArrowRight Alt+Enter when the column is resizable — the first two nudge its width, the last sizes it to its content.
  • Alt+ArrowUp when the column is sortable.
  • Alt+ArrowDown when the column is groupable.

A body cell's aria-keyshortcuts is Space Enter when cell selection is on — see cell selection.

Live announcements#

A role="status" aria-live="polite" region, outside the table itself, reports changes that have no visual cue a screen reader could otherwise pick up on:

  • Column resize — only on release (phase: "end"), not on every frame of the drag: a live region updated on each pointermove would say nothing an assistive technology could keep up with. Announces the column's name and its new width in pixels.
  • Column reorder — on drop. Announces the column's name and its new position, as "column n of total".
  • Sort change — a toggle, a stack, or a clear back to unsorted are each phrased differently: a plain toggle announces the column and its new direction; adding or updating a column in a multi-column sort also announces its priority ("key n of total"); clearing a column's sort announces that its sort was cleared.
  • Row and column selection changes — only when more than one row or column changes in the same interaction (a range, a select-all, Ctrl+A). A single selection is already carried by that row's or column's own aria-selected, so announcing it too would talk over what a screen reader just read. Announces the resulting total, e.g. "3 rows selected".
  • Cell selection — every time a selection lands on a cell. Unlike row and column selection, a cell selection is always exactly one cell, so there's no multi-item threshold to cross — the announcement fires on every commit instead, naming the column and row the selected cell is in.
  • Group-by change — adding, removing, or repositioning a level (via the header toggle, a header dropped on the bar, a dragged chip, or a chip's own Ctrl+Arrow) announces the column and, once two or more levels are stacked, its position ("key n of total"); removing the last level announces that grouping was removed. Every path funnels through the same announcement, so a drag and a click read identically.
  • Group expand/collapse — a single group toggle announces "Group expanded"/"Group collapsed"; expandAllGroups()/collapseAllGroups() announce the whole-tree result instead ("All groups expanded"/"All groups collapsed").

See also#

Edit this page on GitHub