Global Undo / Redo
One keystroke undoes any grid action — sort, filter, quick search, column resize/reorder/hide — not just cell edits, all on a single history. Ctrl+Z to step back, Ctrl+Y to step forward.
One history for every action — not just edits
Set config.undoRedo: true and Ctrl+Z walks back the things you
actually did to the grid: a sort you didn't mean to click, a filter you spent a minute building, a
column dragged to the wrong place. Ctrl+Y (or Ctrl+Shift+Z)
replays them. Sorting, filtering, quick search and column resize/reorder/hide all share
one ordered timeline, so undo follows your real sequence instead of two separate stacks.
Try it
Sort a column, type in the search box, drag a column header, resize one — then press Ctrl+Z a few times and watch each change peel back in order. The Undo / Redo buttons above the grid do the same thing.
Build your own buttons
There are no built-in toolbar buttons — instead the grid exposes everything you need to render your
own, so they match your app. Bind to the canUndo() / canRedo() signals for the
disabled state and call undo() / redo() on click:
<button (click)="grid.undo()" [disabled]="!grid.canUndo()">Undo</button>
<button (click)="grid.redo()" [disabled]="!grid.canRedo()">Redo</button>
<gridcraft #grid [columns]="columns" [dataSource]="ds" [config]="{ undoRedo: true }" />
On a touch device those buttons are how people reach undo — there is no keyboard, so render controls if your users are on tablets or phones.
It respects a cell editor
While you're typing in a cell, Ctrl+Z undoes your text, not the grid — the editor keeps its own undo. The grid's history only takes over once the editor is closed. And undo is scoped to the grid you're working in: two grids on one page don't undo each other.
Off by default
Grid-operation undo is opt-in — set undoRedo: true to turn it on. Existing cell-edit undo
is unaffected: it keeps working with or without this flag.
Cap the depth or narrow the scope
Pass an object instead of true:
readonly config: GridConfig<Person> = {
undoRedo: {
depth: 20, // keep the last 20 steps (default 50)
operations: ['sort', 'filter'], // record only these (default: all)
},
};
Client vs server. With rows in memory (filterApplyMode: 'client', as here)
undo reverts instantly. With a server data source, undoing a sort or filter re-runs the fetch that
restores the earlier view — the same request a fresh sort would make.
Live updates. If a streamed update changes a row that an earlier edit would restore, that step is dropped from the history rather than writing a stale value over newer data — and the change is announced, never silent.
Undo / redo API
| Property | Type | Default | Description |
|---|---|---|---|
| config.undoRedo | boolean | UndoRedoConfig | false | Enable undo/redo for grid operations (sort, filter, quick filter, column changes, grouping, row move). true for defaults, or an object to configure. Cell-edit undo is unaffected by this flag. |
| undoRedo.depth | number | 50 | Maximum number of steps kept; the oldest are dropped past this. |
| undoRedo.operations | GcUndoableOp[] | all | Restrict which operation kinds are recorded. Cell edits are always recorded regardless. |
| grid.canUndo() | Signal<boolean> | — | True when there is a step to undo — bind it to a custom button’s disabled state. |
| grid.canRedo() | Signal<boolean> | — | True when there is a step to redo. |
| grid.nextUndoOp() | Signal<GcUndoableOp | null> | — | The kind of operation the next undo would reverse — for labelling a button. |
| grid.undo() | () => void | — | Reverse the most recent recorded operation. |
| grid.redo() | () => void | — | Re-apply the most recently undone operation. |
| grid.clearHistory() | () => void | — | Drop all recorded steps. |