Keyboard navigation
Pass cellNavigation to navigate the whole table with the keyboard, spreadsheet-style. The table renders with the WAI-ARIA grid pattern: role="grid", a single Tab stop, and a roving tabindex, so the table adds exactly one stop to the page's Tab order and all movement inside it happens with the keys below. Navigation covers every cell, including the header row, selection checkboxes, and expander buttons, so sorting, selecting, and expanding all work without a mouse. It's independent of inline editing and works on read-only tables too.
<DataTable columns={columns} data={data} cellNavigation />;All shortcuts
| Key | Action |
|---|---|
| ← → ↑ ↓ | Move between cells. ↑ from the first body row moves into the header row. Clamped at the edges, no wraparound. |
| Home / End | Jump to the first / last cell in the current row. |
| Ctrl+Home / Ctrl+End | Jump to the first / last cell of the grid. |
| Enter or F2 | Open the focused cell's editor, if it has one (editable/editor — see Inline editing). |
| Escape | Cancel an open editor and return focus to the cell so navigation can continue. |
| Enter or Space on a header | Sort by that column (sortable columns only). |
| Space on a checkbox / Enter on an expander | Toggle row selection / expand or collapse the row. |
Arrow keys only navigate when the focused cell is not being edited. While an editor is open, arrow keys behave normally (moving the text cursor, changing a native<select>, etc.).
Movement: arrows, Home/End, Ctrl+Home/Ctrl+End
The base behavior works on any table, editable or not. Arrow keys move one cell at a time and clamp at the edges — there's no wraparound from the last column back to the first. Homeand End jump to the first / last cell in the current row; add Ctrl(⌘ on macOS) to jump to the first / last cell of the whole grid instead.
Arrow movement
Click a cell, then move with the keyboard only. No editing, sorting, selection, or expansion — just movement.
A plain read-only table. Click a cell, then use ← → ↑ ↓ to move, Home/End to jump to the row edges, and Ctrl+Home/Ctrl+End to jump to the grid corners. No editing, sorting, selection, or expansion here — just movement, which works on any table.
<DataTable columns={columns} data={data} cellNavigation />;Sorting from the keyboard
Arrow ↑ out of the first body row to reach the header. On a sortable column,Enter or Space cycles the sort exactly like a click would — ascending, then descending, then back to unsorted. See Sorting for the full behavior, including multi-column sort.
Sort with Enter / Space
Arrow up to a header, then press Enter or Space to sort. Arrow down returns to the body.
↑ from the first body row moves into the header. Enter or Space on a sortable header cycles ascending → descending → unsorted, same as a click. ↓ from the header returns to the body in the same column.
const columns: TableColumn<Employee>[] = [
{ id: 'name', name: 'Name', selector: r => r.name, sortable: true },
{ id: 'department', name: 'Department', selector: r => r.department, sortable: true },
{ id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, right: true },
];
<DataTable columns={columns} data={data} cellNavigation />;Editing from the keyboard
Enter or F2 opens the focused cell's editor if the column has one (editable or editor). Enter inside the editor commits;Escape cancels. Either way, focus lands back on the cell wrapper so arrow-key navigation continues without an extra click. See Inline editingfor the full set of editor types.
Edit with Enter / F2, cancel with Escape
Arrow to Name or Salary, press Enter or F2 to edit, Enter to commit or Escape to cancel.
Arrow to Name or Salary, then press Enter or F2 to open the editor. Enter commits and Escape cancels — either way, focus returns to the cell so arrow-key navigation continues immediately. Department has no editor, so Enter/F2 do nothing there.
const columns: TableColumn<Employee>[] = [
{ id: 'name', name: 'Name', selector: r => r.name, editable: true, onCellEdit: handleCellEdit },
{ id: 'department', name: 'Department', selector: r => r.department }, // no editor
{ id: 'salary', name: 'Salary', selector: r => r.salary, right: true, editable: true, onCellEdit: handleCellEdit },
];
<DataTable columns={columns} data={data} cellNavigation />;Selection and expansion from the keyboard
Selection checkboxes and expander buttons sit in the nav grid like any other cell. Arrow onto a row's checkbox and press Space to select it — arrow up far enough and you reach the header's select-all checkbox, which works the same way. Arrow onto an expander button and pressEnter to expand or collapse that row.
Select and expand without a mouse
Everything together: arrows, Home/End, sorting, editing, row selection, and row expansion, all from the keyboard.
Click or Tab into the table, then use only the keyboard: arrows to move, Enter/Space to sort a header, Space to select a row, Enter to expand one, and Enter/F2 to edit Name or Salary.
<DataTable
columns={columns}
data={data}
cellNavigation
selectableRows
expandableRows
expandableRowsComponent={RowDetail}
/>;The grid pattern
Without cellNavigation, the table renders role="table" with plainrole="cell" cells — the right shape for a screen reader's native table-reading commands, since nothing in the table is meant to hold focus. Turning on cellNavigationswitches every region to the WAI-ARIA grid pattern: role="grid", role="gridcell" for body cells, and a single roving tabIndex={0} that follows the last-focused cell. This is why it's an opt-in prop rather than always-on — it's the right choice for tables you expect people to operate, not a strict upgrade over the default.
See Accessibility for the full ARIA role reference, including how roles differ with and without cellNavigation.
Limitations
- Clamps at row/column edges rather than wrapping.
- Doesn't cross page boundaries — arrowing past the last row does not auto-paginate.
- Key bindings are fixed. There's no prop to remap them.
Prop reference
| Table prop | Type | Description |
|---|---|---|
cellNavigation | boolean | Enable spreadsheet-style keyboard navigation (WAI-ARIA grid pattern: role='grid', single Tab stop, roving tabindex). Defaults to false. |