Selection & keyboard
Range selection, mouse gestures, and the full keyboard shortcut reference.
Selection model
A selection has an anchor cell (the active cell, Excel's white cell) and a rectangular range around it. With multi-range enabled, it also has a stack of additional ctrl-clicked ranges. Rows and columns also have their own whole-row and whole-column selection channels, through marker-column checkbox clicks and header clicks, independent of the range.
Checked rows follow their records when a sort, filter, or new data array rebuilds the view.
When a row leaves the view, the grid unchecks it. Row ids must be unique: when two rows share an
id, the check follows the first row with that id. The active cell follows its record and its column
when the view rebuilds and when columns move, hide, or pin. When its row or its column leaves the
view, the active cell keeps its view position, clamped into the view. When a rebuild puts other
records or other columns under the range, the grid collapses the range onto the active cell. A
clamp that moves the active cell also collapses the selection onto it. A clamp that keeps the
active cell in place drops the range. A row operation never reaches a record that you did not select.
Without checked rows or a range, a row operation acts on the active cell's row. Selected columns
follow their column when columns move, hide, or pin, and a column that leaves the view leaves the
selection.
Selection overlays are placed on grid lines and render with zero cell re-renders. Dragging a selection never re-renders a single cell.
Mouse gestures
- Plain left-button press and drag from any cell, active or not, live-paints a rectangular range from the press origin to the hovered cell, in any direction.
- Shift+click extends the range from the current anchor to the clicked cell.
- Header press and drag selects a contiguous multi-column range. A plain header click
selects the whole column, or sorts it if
headerClickBehavior="sort". - Marker press and drag, with
rowMarkersenabled, selects multiple rows. The range follows the pointer from the press row, so dragging back over selected rows de-selects them again. In thereorder-family marker modes only a drag that starts on the drag handle zone reorders the row, and it leaves the selection as it was. Drags from the number or checkbox glyph still select. - Shift+click on a row marker, on its number or on its checkbox, checks the rows from the anchor row to this row. See Checking a range of rows.
- Ctrl/Cmd-click adds a new range to the stack (
enableMultiRange, on by default).
The grid body suppresses native browser text selection during these gestures. Editors remain normally text-selectable.
Checking a range of rows
The row that you checked or unchecked last is the anchor. A Shift step checks the rows from the anchor to the target row. Each Shift step replaces the previous Shift step, so the range grows and shrinks. Rows outside the range keep their state, and checked rows that a filter hides stay checked.
| Gesture | Result |
|---|---|
| Click a checkbox | Toggles the row. The row becomes the anchor. |
| Click a number | Checks only this row. The row becomes the anchor. |
| Ctrl/Cmd+click | Toggles the row and keeps the other checked rows. The row becomes the anchor. |
| Shift+click | Checks the rows from the anchor to the clicked row. If the anchor row is unchecked, the range unchecks its rows. |
| Ctrl/Cmd+Shift+click | Keeps the current range and adds the rows from the anchor to the clicked row. The next Shift step replaces only the new range. |
| Press a number or a checkbox and drag | Moves the end of the range with the pointer. A drag that starts on a checked checkbox unchecks the rows. |
| Shift+press and drag | Moves the end of the range from the anchor row. |
| Shift+click without an anchor | Starts at the active row. Without an active row, it checks only the clicked row. |
A sort or a filter ends the range. The next Shift+click then starts from the anchor row. The header checkbox and Escape clear the anchor, so the next Shift+click starts at the active row.
For example, click row 1, Shift+click row 5, and Shift+click row 3. Rows 1 to 3 are checked.
Touch gestures
A touch screen scrolls the grid natively. The grid reads the input type of each press, so a laptop with a touch screen and a trackpad gets touch gestures from the screen and mouse gestures from the trackpad.
| Gesture | Result |
|---|---|
| Tap a cell | Selects the cell. A scroll or a pan selects nothing. |
| Double-tap a cell | Starts editing, or toggles a checkbox cell. A read-only grid does not edit. |
| Tap a header | Selects the column, or sorts it with headerClickBehavior="sort". |
| Tap a row marker | Selects the row. |
| Long-press (hold for 500 ms) | Opens the context menu on the cell or the header. |
| Drag a selection handle | Extends the range from the opposite corner. The grid scrolls at the edges. |
After a touch selection, two selection handles appear at the top-start and bottom-end corners of the range. Any key press hides them. A press must stay within 10 px to count as a tap or a long-press.
The grid sets touch-action: manipulation. A double-tap does not zoom the page, and pinch zoom
stays available. Use density="comfortable" for rows that are easier to hit with a finger.
The demo has the fill and context menu add-ons:
Use a touch device or browser device mode. Drag a handle to select several cells, then press and hold to open the context menu. Fill down and Fill right appear for a touch selection.
Configurability
You can disable each selection channel independently through DataGridProvider props. All
default to true:
<DataGridProvider
enableRowSelection={true}
enableColumnSelection={true}
enableRangeSelection={true}
enableMultiRange={true}
{...grid}
columns={columns}
>Disabling a channel removes its state, not just its gestures
enableColumnSelection={false} makes header clicks do nothing, rather than only hiding a
highlight. The column selection channel itself is gone.
Checked rows
A checked row is a row whose marker checkbox is on. A click outside the grid, or in the empty space of the grid, clears the cell range and the column selection. It never clears checked rows. A toolbar button outside the grid reads the checked rows after it is clicked.
Selecting a cell clears the checked rows. Set keepCheckedRows to keep them:
<DataGridProvider keepCheckedRows {...grid} columns={columns}>With keepCheckedRows, a cell click, an arrow key, and a Shift or Ctrl click leave the checked rows as they are. Escape, the marker checkboxes, the header checkbox, clearSelection(), and deleting a row change them.
While at least one row is checked, row operations act only on the checked rows, never on the range. This covers Delete rows, Duplicate row, Move rows, and the row items of the cell context menu. Cell operations such as copy, cut, fill, and Delete stay on the cell range. With no checked row, row operations use the range.
A filter removes the checked rows it hides. Set keepHiddenCheckedRows to remember them:
<DataGridProvider keepCheckedRows keepHiddenCheckedRows {...grid} columns={columns}>A hidden checked row is a checked row that the current filter hides. The grid checks it again when the filter lets it back in. A sort keeps both the visible and the hidden checks.
The demo sets both props:
Check a few rows, then open the Role filter and keep one role. The status bar counts the checked rows the filter hides. Clear the filter and the checks come back.
While every checked row is hidden, Delete rows, Duplicate row, and Move rows do nothing, and their context menu items are disabled. Row operations never touch a hidden checked row. Delete rows, Duplicate row, Move rows, the context menu, the export scope 'selection', and the clipboard read only the checked rows you can see. Select all checks the rows of the view and adds no hidden row. Escape, clearSelection(), setSelection(null), an empty controlled selection, and the header checkbox set to off clear the hidden checks too.
The controlled selection prop and onSelectionChange carry the visible rows only. useDataGridHiddenCheckedCount() returns the number of hidden checked rows. DataGridStatusBar shows it as (+3 hidden) beside the selection stats. With fewer than two cells selected, it shows 3 hidden checked rows. The labels are labels.statusBar.hiddenChecked and labels.statusBar.hiddenCheckedOnly.
The grid remembers at most 100,000 hidden checked rows. A server-sorted grid ignores the option. A grid with unloaded rows (a lazy source) ignores it while its data holds them. Both warn once in development. Switching to sortMode="server" clears the hidden checks.
setSelection with a non-empty value and a non-empty controlled selection change the visible checks and keep the hidden ones.
| Prop | Type | Default |
|---|---|---|
keepCheckedRows | boolean | false |
keepHiddenCheckedRows | boolean | false |
announceResults New | boolean | false |
onBeforeDeleteRows | (ctx: { rowIds: readonly string[]; hiddenCount: number }) => boolean | Promise<boolean> | none |
keepHiddenCheckedRows takes effect only with keepCheckedRows. announceResults reads the row count of the view after a filter or sort change; see Accessibility. onBeforeDeleteRows is described in Row operations.
Header click behavior
headerClickBehavior on DataGridProvider picks what a plain header click does. Header
press and drag always multi-selects columns, regardless of this setting.
| Value | A plain header click... |
|---|---|
"select" (default) | Selects the whole column, through the column selection channel. |
"sort" | Cycles that column's sort between ascending, descending, and clear, instead of selecting. See Sorting, filtering & search. |
"none" | Does nothing. Use this when you drive sort exclusively through data-grid-sort-list or your own UI, and you do not want header clicks to also select or sort. |
<DataGridProvider headerClickBehavior="none" {...grid} columns={columns}>Try all three values live in the playground.
Per-column sortable: false still removes that one column from the click-to-sort gesture, even
under headerClickBehavior="sort". You can still sort it programmatically through
useDataGridActions().setSorts().
Keyboard map
The keymap is a plain data table, the keymap prop, merged over DEFAULT_KEYMAP. You can remap
or extend any binding without touching the engine. The remapping surface and the in-app
shortcuts dialog are on the Keybindings page. The table below is generated
from the shipped defaults.
Navigation
| Move up | ↑ |
| Move down | ↓ |
| Move left | ← |
| Move right | → |
| Move up (keep selection) | Alt↑ |
| Move down (keep selection) | Alt↓ |
| Move left (keep selection) | Alt← |
| Move right (keep selection) | Alt→ |
| Scroll active cell into view | CtrlEnter |
| Move to row start | Home |
| Move to row end | End |
| Jump to edge (up) | Ctrl↑ |
| Jump to edge (down) | Ctrl↓ |
| Jump to edge (left) | Ctrl← |
| Jump to edge (right) | Ctrl→ |
| Move to first cell | CtrlHome |
| Move to last cell | CtrlEnd |
| Page up | PageUp |
| Page down | PageDown |
Selection
| Extend selection up | Shift↑ |
| Extend selection down | Shift↓ |
| Extend selection left | Shift← |
| Extend selection right | Shift→ |
| Extend selection to edge (up) | CtrlShift↑ |
| Extend selection to edge (down) | CtrlShift↓ |
| Extend selection to edge (left) | CtrlShift← |
| Extend selection to edge (right) | CtrlShift→ |
| Extend selection to first cell | CtrlShiftHome |
| Extend selection to last cell | CtrlShiftEnd |
| Check rows up to the first row | ShiftHome |
| Check rows down to the last row | ShiftEnd |
| Check rows up one page | ShiftPageUp |
| Check rows down one page | ShiftPageDown |
| Select row | ShiftSpace |
| Select column | CtrlSpace |
| Select all | CtrlA |
| Check or uncheck row | CtrlShiftSpace |
| Check or uncheck all rows | CtrlShiftE |
| Open column menu | ShiftAlt↓ |
| Open column filter | ShiftAlt↑ |
Editing
| Edit cell | EnterF2Space |
| Commit and move down (editor) | Enter |
| Commit and move up (editor) | ShiftEnter |
| Leave the grid forward (Tab) | Tab |
| Leave the grid backward (Shift+Tab) | ShiftTab |
| Cancel edit | Escape |
| Delete contents | DeleteBackspace |
| Insert row below | CtrlShiftF |
| Duplicate row | CtrlShiftX |
| Insert row above | CtrlShiftU |
| Delete row(s) | CtrlShiftBackspace |
| Move rows up | ShiftAltPageUp |
| Move rows down | ShiftAltPageDown |
| Open link | AltEnter |
Clipboard & Fill
| Fill down | CtrlD |
| Fill right | CtrlR |
| Copy | CtrlC |
| Cut | CtrlX |
| Paste | CtrlV |
History
| Undo | CtrlZ |
| Redo | CtrlYCtrlShiftZ |
mod resolves to Cmd on macOS and Ctrl elsewhere. Copy, Cut, and Paste
(Ctrl/Cmd+C/X/V) are native browser clipboard events, not part of the
keymap, but the list above includes them because the shortcuts dialog shows them together.
Escape outside edit mode clears the whole selection
The Esc row is labeled "Cancel edit", but with no editor open the cancel action
clears the entire selection: the cell range and its stack, plus any whole-row and whole-column
selection. To keep a selection across an Escape, remap or drop the cancel binding through the
keymap prop.
Undo and redo outside the grid
The in-grid undo and redo bindings above work only while focus is inside the grid.
Global shortcuts extends them to a custom toolbar or
anywhere else on the page: mount DataGridGlobalShortcuts inside DataGridRoot and the same
bindings reach the grid while focus is outside.
Checking rows by keyboard
The marker checkboxes are not tab stops, so the keyboard has its own actions:
- Ctrl+Shift+Space (
toggleRow) checks or unchecks the active row. The other checked rows stay checked. - Mod+Shift+E (
toggleAllRows) checks every row, or unchecks them when every row is checked. It does what the header checkbox does. - Shift+Space (
selectRow) replaces the selection with the active row. - After
selectRowortoggleRow, Shift+ArrowUp and Shift+ArrowDown (extendUp,extendDown) move the end of the range and the active row, and check the rows from the anchor to the new end. They shrink the range when you reverse. After a click on a cell, the same keys extend the cell range. - In the same state, Shift+Home (
extendRowsToStart), Shift+End (extendRowsToEnd), Shift+PageUp (extendRowsPageUp), and Shift+PageDown (extendRowsPageDown) move the end of the range to the first row, the last row, or one page away. Without a row check, these keys do nothing, and the browser keeps them.
To check rows that are not next to each other, move with Alt+Arrow, which
keeps the selection, and press toggleRow on each row. Ctrl/Cmd+A
(selectAll) selects cells, not checked rows.
A check or uncheck New by toggleRow, toggleAllRows, the Shift+Arrow range keys, a marker checkbox, a marker range drag, or the header checkbox reads the number of checked rows in the grid's live region, for example "3 rows checked" (labels.grid.rowsChecked). Escape reads the drop too. The count includes the hidden checked rows that keepHiddenCheckedRows holds.
Moving rows
Shift+Alt+PageUp (moveRowsUp) and
Shift+Alt+PageDown (moveRowsDown) move the checked rows one place.
When no row is checked, they move the rows of the selected range, or the active row. They need a
reorder-family rowMarkers mode. See Row operations.
Opening the column filter
Shift+Alt+ArrowUp (openColumnFilter) opens the header filter of the
active cell's column. It does nothing unless the root has a renderHeaderFilter slot and the column offers a set filter.
The Shift+Alt+ArrowDown chord opens the column menu.
Two-stage Ctrl+A
Two-stage select-all
The first Ctrl/Cmd+A selects the current data region. Press it again to select the whole grid.
Accessibility
The full aria model, the conformance target, and the honest test status matrix are on the Accessibility page.