Grid Keyboard
Keyboard
Full keyboard contract — 2D arrow nav, shift-extend, cmd-jump, Tab wrap, Cmd+A, Esc.
<PretableSurface> ships the full ARIA grid keyboard pattern out of the box. The grid is a single tab stop in the page tab order — once focus enters, every key below works without additional wiring. Cmd/Ctrl is treated as either platform's primary modifier.
Full keyboard contract
| Key | Action |
|---|---|
↑ / ↓ / ← / → | Move focus by one cell. Collapses selection. |
Shift + Arrow | Extend the active range by one cell. |
Cmd/Ctrl + Arrow | Jump focus to grid edge in arrow direction. |
Cmd/Ctrl + Shift + Arrow | Extend range to grid edge. |
Home / End | Move focus to first / last column in current row. |
Cmd/Ctrl + Home / End | Move focus to first / last cell in grid. |
PageUp / PageDown | Move focus by viewport height. |
Shift + PageUp/Down | Extend range by viewport height. |
Tab (default "wrap-rows") | Move right, wrap to next row at end. |
Shift + Tab | Move left, wrap to prev row at start. |
Tab (when tabBehavior="exit") | Browser default — focus leaves the grid. |
Cmd/Ctrl + A | Select all cells. |
Cmd/Ctrl + C | Copy current selection (see Clipboard). |
Esc | Collapse selection to focused cell. |
Enter / Space | Toggle full-row selection on focused row. |
tabBehavior config
tabBehavior controls what Tab and Shift+Tab do inside the grid:
"wrap-rows"(default) — Tab moves focus right; at the last column it wraps to the first column of the next row. Shift+Tab is the reverse. Tab never leaves the grid via this path; use the browser's normal focus order from outside."exit"— strict ARIA grid behavior. Tab and Shift+Tab fall through to the browser's default focus traversal, leaving the grid entirely. Use this when the grid sits inside a form and Tab should advance to the next field.
<PretableSurface tabBehavior="exit" /* ... */ />Both behaviors preserve the single-tab-stop model — once Tab leaves, the grid keeps its last focused cell. Coming back via Shift+Tab restores focus to that cell.
Single tab stop / focus model
<PretableSurface> follows the ARIA grid roving tabindex pattern. At any moment exactly one cell has tabIndex={0}; every other cell has tabIndex={-1}. The 0-tabindex cell is the focused cell from snapshot.focus.
When the focused address changes (via keyboard, click, or programmatic grid.setFocus), a useLayoutEffect calls .focus() on the new cell's DOM node, so the browser's focus ring follows the engine state without flicker. Scrolling adjusts to keep the focused cell on-screen if needed.
This means consumers don't have to manage tabIndex themselves when using <PretableSurface>. If you're building with usePretable and rendering your own JSX, mirror the pattern:
const isFocused =
snapshot.focus.rowId === row.id && snapshot.focus.columnId === col.id;
<div data-pretable-cell="" tabIndex={isFocused ? 0 : -1} /* ... */>
…
</div>;Customizing key behavior
Pretable doesn't expose individual key handlers — there's no onArrowDown or onCopyKey prop. If you need to intercept (for example, to add Vim-style j/k bindings, or to swallow Cmd+A in a specific mode), wrap the surface in your own keydown listener and call event.preventDefault() on the keys you handle. Most consumers don't need this.
For programmatic moves from outside the grid (a button, a command palette, telemetry replay), call the model directly:
grid.setFocus({ rowId, columnId });
grid.moveFocus("down");
grid.moveFocus("right", { extend: true }); // shift+right equivalent
grid.moveFocus("down", { jumpToEdge: true }); // cmd+down equivalent
grid.selectAll();
grid.clearSelection();See API reference for the full method list.
See also
- Selection — the cell-range model the keyboard mutates.
- Clipboard —
Cmd/Ctrl + Csemantics and overrides. - API reference —
moveFocus,setFocus,selectAll,clearSelectionsignatures.