gridcn

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.

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 rowMarkers enabled, selects multiple 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.

Name
Email
Age
Active
Role
Joined
Score
Silent Tiger
user0@example.com
63
Viewer
Jul 5, 2023
4
Quick Lion
user1@example.com
67
Viewer
Apr 3, 2021
89
Swift Wolf
user2@example.com
40
Manager
Oct 27, 2020
80
Eager Lion
user3@example.com
50
User
Oct 8, 2022
51
Eager Tiger
user4@example.com
45
Viewer
Oct 7, 2024
83
Silent Wolf
user5@example.com
24
User
Jun 1, 2020
15
Swift Lion
user6@example.com
34
Viewer
Oct 18, 2022
62
Eager Lion
user7@example.com
21
Manager
Oct 7, 2021
42
Eager Eagle
user8@example.com
25
Editor
Jun 11, 2024
35
Bold Tiger
user9@example.com
19
Editor
Mar 20, 2024
52
Bold Bear
user10@example.com
42
User
Apr 16, 2020
28
Swift Wolf
user11@example.com
47
Manager
Aug 4, 2022
9

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.

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.

ValueA 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 table below is generated from the shipped defaults.

pnpm dlx shadcn@latest add @gridcn/data-grid-keybindings

Show this table at runtime

data-grid-keybindings displays this same table as an in-app dialog, always reflecting the actual effective keymap of your grid, including overrides. The dialog reads the grid's keymap and labels through the store, so it mounts inside DataGridProvider. The ? shortcut reads the root's container element, so it mounts inside DataGridRoot:

import { useState } from "react";
import {
  DataGridKeybindingsDialog,
  DataGridKeybindingsShortcut,
} from "@/components/data-grid-keybindings/data-grid-keybindings";

function OrdersGrid() {
  const [shortcutsOpen, setShortcutsOpen] = useState(false);

  return (
    <DataGridProvider {...grid} columns={columns}>
      <DataGridKeybindingsDialog open={shortcutsOpen} onOpenChange={setShortcutsOpen} />
      <DataGridRoot>
        <DataGridHeader />
        <DataGridBody />
        <DataGridKeybindingsShortcut onOpen={() => setShortcutsOpen(true)} />
      </DataGridRoot>
    </DataGridProvider>
  );
}

The ? shortcut is a contested key

While DataGridKeybindingsShortcut is mounted and the grid has focus, it claims ? (and only outside an open cell editor): type-to-edit and any keymap binding on ? are shadowed, because the shortcut stops the native event before the grid sees it. Without the shortcut component, ? type-to-edits into the active cell like any other printable character.

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
Select row
ShiftSpace
Select column
CtrlSpace
Select all
CtrlA

Editing

Edit cell
EnterF2Space
Commit and move down (editor)
Enter
Commit and move up (editor)
ShiftEnter
Commit and move right (editor)
Tab
Commit and move left (editor)
ShiftTab
Cancel edit
Escape
Delete contents
DeleteBackspace
Insert row below
CtrlShiftF
Duplicate row
CtrlShiftX

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.

Remapping

<DataGridProvider {...grid} columns={columns}>
  <DataGridRoot keymap={{ selectAll: ["mod+shift+a"] }} />
</DataGridProvider>

keymap lives on DataGridRoot (or the DataGrid wrapper), not on DataGridProvider. This overrides only the actions you specify. Everything else falls back to DEFAULT_KEYMAP.

The type-to-replace trigger (editReplace) is remapped the same way. By default it has no binding: any unbound printable key on the active cell dispatches it and opens the editor seeded with the typed character (Excel behavior). Defining it in keymap takes the action over exclusively — ANY editReplace binding suppresses the implicit printable-key fallback entirely, there is no additive mode: keymap={{ editReplace: ["F3"] }} means type-to-replace only happens on F3 (a plain edit start), and keymap={{ editReplace: [] }} disables it entirely. A printable key bound to editReplace (e.g. "a") starts the edit seeded with that character, Excel-style.

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 grid renders role="grid", with aria-rowindex, aria-colindex, aria-rowcount, and aria-colcount on cells, and a single roving-tabindex active cell backed by real DOM focus. Focus never gets lost when the active cell scrolls out of the rendered window.

See Accessibility for the full aria model, the conformance target, and the honest test status matrix.

On this page