Home/Editing/Batch / Bulk Editing
Editing

Batch / Bulk Editing

Apply one value to many rows at once — through the same validated commit path as a single edit, as one undoable action. Drive it from the applyToSelectedRows API or with a spreadsheet-style Ctrl/Cmd+Enter while editing.

Apply one value to the selection

Tick a few rows, choose a column and a value, and press Apply. One call — grid.applyToSelectedRows(colKey, value) — writes every selected row and returns { committed, skipped }. Wire it to any toolbar, bulk-action menu or shortcut you like.

Try it: tick a few rows  ·  pick a column and a value  ·  press Apply to selected
ID
Name
Department
Status
Location
Salary
1
Alice Smith
Engineering
Active
Remote
92000
2
Bob Johnson
Engineering
Active
Berlin
88000
3
Carol Williams
Design
On leave
Cairo
79000
4
David Brown
Product
Active
London
101000
5
Emma Jones
Support
Contractor
Remote
64000
6
Frank Miller
Engineering
Active
Austin
96000
7
Grace Davis
Sales
Active
Remote
72000
8
Henry Wilson
Design
Active
Cairo
81000
9
Isabel Clark
Product
On leave
Berlin
99000
10
Jack Turner
Support
Active
London
67000
All records loaded
10 / 10 records

applyToSelectedRows(colKey, value) returns { committed, skipped } — the numbers, not a boolean, because a mixed selection is the normal case. With no selection it is a no-op returning { committed: 0, skipped: 0 } rather than throwing, so a toolbar button needs no guard of its own.

Per-row (cellEditCommit) is suppressed inside a batch. One (batchEditCommit) carries rowIds, so the server sees one request instead of N — which is the point of a bulk action.

Ctrl/Cmd+Enter fills the selection

The spreadsheet gesture, and the same code path underneath. Select rows, start editing a cell in one of them, pick a value, then press Ctrl (or Cmd) + Enter instead of Enter — every selected row takes it. No wiring needed: config.batchEdit turns the modifier on.

Try it: tick rows 1–3  ·  double-click a Location cell in row 1  ·  pick a value  ·  press Ctrl/Cmd+Enter
ID
Name
Department
Status
Location
Salary
1
Alice Smith
Engineering
Active
Remote
92000
2
Bob Johnson
Engineering
Active
Berlin
88000
3
Carol Williams
Design
On leave
Cairo
79000
4
David Brown
Product
Active
London
101000
5
Emma Jones
Support
Contractor
Remote
64000
6
Frank Miller
Engineering
Active
Austin
96000
7
Grace Davis
Sales
Active
Remote
72000
8
Henry Wilson
Design
Active
Cairo
81000
9
Isabel Clark
Product
On leave
Berlin
99000
10
Jack Turner
Support
Active
London
67000
All records loaded
10 / 10 records

There is one code path. The modifier and the API raise the same (batchEditCommit) and write through the same commit gate; the gesture is only a second way to reach it.

config.batchEdit: false removes the modifier and leaves the API working — useful when Ctrl+Enter already means something in your app. Plain Enter always commits just the one cell, so nothing is filled by accident.

One batch, one undo step

A batch of any size is a single entry in the history, so one Ctrl+Z takes all of it back — not one row per press. That is the difference between a bulk action and a loop that happens to call commit N times.

Apply, then undo once.
ID
Name
Department
Status
Location
Salary
1
Alice Smith
Engineering
Active
Remote
92000
2
Bob Johnson
Engineering
Active
Berlin
88000
3
Carol Williams
Design
On leave
Cairo
79000
4
David Brown
Product
Active
London
101000
5
Emma Jones
Support
Contractor
Remote
64000
6
Frank Miller
Engineering
Active
Austin
96000
7
Grace Davis
Sales
Active
Remote
72000
8
Henry Wilson
Design
Active
Cairo
81000
9
Isabel Clark
Product
On leave
Berlin
99000
10
Jack Turner
Support
Active
London
67000
All records loaded
10 / 10 records

The history records the batch as one compound entry, so undo and redo move over the whole set. A loop calling commit per row would record N entries and leave the reader pressing Ctrl+Z ten times — with a half-reverted table at every step in between.

Undo here is the grid's ordinary edit history: Ctrl+Z works without config.undoRedo, which is about widening the history to sorts, filters and column moves — a different question.

Validation runs per row, inside the batch

Salary declares validate: { min: 0 }. Push a negative value across the whole selection and the rule refuses those rows individually — the batch is neither all-or-nothing nor unchecked. Switch to allow mode and the same rows commit, but stay marked.

Every row is selected for you.
ID
Name
Department
Status
Location
Salary
1
Alice Smith
Engineering
Active
Remote
92000
2
Bob Johnson
Engineering
Active
Berlin
88000
3
Carol Williams
Design
On leave
Cairo
79000
4
David Brown
Product
Active
London
101000
5
Emma Jones
Support
Contractor
Remote
64000
6
Frank Miller
Engineering
Active
Austin
96000
7
Grace Davis
Sales
Active
Remote
72000
8
Henry Wilson
Design
Active
Cairo
81000
9
Isabel Clark
Product
On leave
Berlin
99000
10
Jack Turner
Support
Active
London
67000
All records loaded
10 / 10 records

The rule is declared once on the column and runs at the commit gate, so a single inline edit and a thousand-row batch pass exactly the same check. Nothing about the batch path is special-cased, which is why this claim is worth stating: bulk operations are where validation is usually skipped.

The check is per row. In block mode a failing row is left exactly as it was and counted in skipped, while its valid neighbours in the same selection still commit — the batch is neither all-or-nothing nor unchecked. In allow mode every row commits and the failing ones stay marked until a valid value replaces them.

Client-side rules are a courtesy, not a boundary. Persist from (batchEditCommit) and revalidate on the server before writing — a bulk endpoint is exactly where one bad row is most expensive.

Why this is one action and not many

A bulk edit that loops over the selection calling commit per row gives you N history entries, N events for the server, and N chances to end up half-applied. Everything on this page is the other thing: one validated pass, one history entry, one event, one screen-reader announcement. A batch of any size lands as a single grid update and speaks once, never once per row.

Both routes in — the API and Ctrl/Cmd+Enter — are the same code path, and both are free. Row selection, which supplies the target set, is free too.

Batch Editing API

PropertyTypeDefaultDescription
applyToSelectedRows(colKey, value)(string, any) => BatchEditResult—Apply value to colKey across all selected rows as one undoable batch. Returns { committed, skipped }. No selection → no-op.
(batchEditCommit)BatchEditEvent—Fired once per batch: { colKey, value, rowIds, committed, skipped }. Per-row (cellEditCommit) is suppressed inside a batch.
config.batchEditbooleantrue (with row selection)Enables the Ctrl/Cmd+Enter in-editor shortcut. The API works regardless of this flag.
config.editValidation'block' | 'allow''block'block — invalid rows are skipped and counted. allow — invalid rows commit but stay marked. Checked per row, inside the batch.
config.rowSelectionRowSelectionConfig—The (free) selection is the batch target set. Multi-select the rows, then apply.