# History Undo/Redo
URL: https://pro.rv-grid.com/guides/data-manage/history/
Source: src/content/docs/guides/data-manage/history.mdx
Description: History and undo and redo capabilities in data grid, making it easier to manage user edits
The History Plugin brings powerful undo and redo capabilities to your data grid, making it easier to manage user edits and maintain data integrity.

Keyboard shortcuts like `Ctrl+Z` (or `Cmd+Z` on macOS) for undo and `Ctrl+Y` (or `Cmd+Y` on macOS) / or `Ctrl+Shift+Z` (or `Cmd+Shift+Z` on macOS) for redo are supported out of the box.

Shortcuts work from document keydown events by default, even when the grid does not currently have a focused cell. Set `keyboardShortcutsWithoutFocus: false` to require grid focus.

You can customize its behavior by using the BEFORE_UNDO_EVENT and BEFORE_REDO_EVENT hooks.

- Undo/Redo Management: Automatically tracks user changes with the ability to undo/redo actions using keyboard shortcuts.
- Configurable Stack Size: Tracks up to 200 changes by default, and accepts partial `history` config when you only need to override one option.
- Provider Persistence: Can load and save stack state through local cache and custom server providers for same-dataset sessions.
- Custom Event Hooks: Supports custom behavior via beforeundo and beforeredo events, allowing full control over the undo/redo process.
- Pro Editing Coverage: Tracks regular cell edits, Pro editor templates, textarea editors, range edits, clipboard paste, autofill, row-edit saves, and audit restore replays when paired with `EventManagerPlugin`.

### Additional details

Use `EventManagerPlugin` together with `HistoryPlugin`.
`HistoryPlugin` listens to `ON_EDIT_EVENT`, which is emitted by
`EventManagerPlugin`.

```ts
import {
  EventManagerPlugin,
  HistoryPlugin,
  createLocalStorageHistoryAdapter,
} from '@revolist/revogrid-pro';

plugins = [EventManagerPlugin, HistoryPlugin];
```

Undo, redo, and public replay operations emit `beforehistedit`. When `EventManagerPlugin` is installed, it handles that event and emits the normal `gridedit` flow. When it is not installed, History applies the replay directly with `setRangeData`.

`AuditHistoryPlugin` automatically reuses or installs `HistoryPlugin` for restore replay, keyboard shortcuts, and existing History integrations. Keep History configuration on `grid.history`; Audit History does not define a nested History config.

`grid.history` is optional. Add it only when you need custom stacks, a smaller
or larger stack limit, or an initially disabled plugin.

```ts
grid.history = {
  maxStackSize: 100,
  keyboardShortcutsWithoutFocus: false,
};
```

### Persisting History

Undo and redo stacks are index-based, so persisted history is safe only when the user returns to the same dataset and row order. For long-term recovery across changing data, use `AuditHistoryPlugin`, which restores by stable row identity.

Use `sourceId` to reject stale server states. With multiple providers, the plugin loads the first provider immediately, then applies later providers only when their `updatedAt` state is newer and their `sourceId` is compatible.

When storage is configured, `clearOnSourceChange` defaults to `false` so the initial data load does not erase the restored stack. Set it to `true` only when your app wants every source replacement to clear persisted undo/redo state.

```ts
grid.history = {
  sourceId: `invoices:${workspaceId}:${datasetVersion}`,
  storageProviders: [
    createLocalStorageHistoryAdapter('invoices:history'),
    {
      async load() {
        const response = await fetch('/api/grid-history');
        return response.json();
      },
      async save(state) {
        await fetch('/api/grid-history', {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(state),
        });
      },
      async clear() {
        await fetch('/api/grid-history', { method: 'DELETE' });
      },
    },
  ],
};
```

Listen to `historyerror` when you want to surface provider failures or skipped states:

```ts
grid.addEventListener('historyerror', (event) => {
  console.warn(event.detail.phase, event.detail.error);
});
```

To call `undo()`, `redo()`, `clear()`, `disable()`, or state helpers, get plugin instances
from the grid and find the `HistoryPlugin` instance.

```ts

async undo() {
  // get plugins from the grid
  const plugins = await this.gridRefNativeElement.getPlugins();
  const history = plugins.find((plugin) => plugin instanceof HistoryPlugin);
  if (history) {
    if (history.canUndo()) {
      history.undo();
    }
  }
}
```

For custom controls, listen to `historychanged` instead of reading plugin
internals. The event detail includes `undoStackSize`, `redoStackSize`,
`canUndo`, `canRedo`, and `disabled`.

### Troubleshooting

- Undo/redo does not record changes: ensure plugin order is
  `[EventManagerPlugin, HistoryPlugin]`.
- Restore flashes are missing in Audit History: install `EventManagerPlugin` and `CellFlashPlugin`; Audit restore replay flows through `beforehistedit` and the normal edit event path.
- Calling plugin methods does not work: call `getPlugins()` first, then find
  `HistoryPlugin`.
- History clears after sort, filter, row-order, or source replacement: this is
  intentional because those operations can change visible row indexes.
- Persisted history was skipped: check the `historyerror` event for a
  `storage-source-mismatch` phase and update the configured `sourceId`.
- Need a known-good reference: see code in:
  `revogrid-pro/src/components/history/**.**`.

### Key methods

The History Plugin provides four key methods to enhance control over `undo/redo` functionality:
- `clear()`: Resets both the undo and redo stacks, clearing all recorded changes.
- `disable(disable = true)`: Temporarily disables the plugin, preventing changes from being recorded or undo/redo operations from being triggered.
- `undo()`: Reverts the last change recorded in the undo stack and moves it to the redo stack for potential reapplication.
- `redo()`: Reapplies the most recent change from the redo stack, moving it back to the undo stack for further management.
- `canUndo()` / `canRedo()`: Returns whether undo or redo is currently available.
- `getUndoStackSize()` / `getRedoStackSize()`: Returns stack counts for custom controls.
- `isDisabled()`: Returns whether history is disabled.

These methods give developers flexibility in managing user actions and tailoring the plugin’s behavior to specific application requirements.

### Behind the Scenes

The plugin listens to edit events (onEditEvent) and tracks changes in undo and redo stacks. Keyboard shortcuts like Ctrl+Z for undo and Ctrl+Y (or Ctrl+Shift+Z) for redo are supported out of the box. You can customize its behavior by using the BEFORE_UNDO_EVENT and BEFORE_REDO_EVENT hooks.

Here’s a quick snippet showing how the plugin processes undo actions:

```ts
undo() {
  if (this.undoStack.length === 0) return;
  const lastChange = this.undoStack.pop();
  if (lastChange) {
    const event = this.emit(BEFORE_UNDO_EVENT, {
      data: lastChange.previousData,
      type: lastChange.type,
      lastChange,
    });
    if (!event.defaultPrevented) {
      const data = event.detail.data || lastChange.previousData;
      this.replayChange({
        data,
        type: event.detail.type || lastChange.type,
        models: lastChange.models,
        previousData: lastChange.data,
      });
      this.redoStack.push(lastChange);
    }
  }
}
```

`replayChange()` emits `beforehistedit` first. If no integration handles that event, History falls back to `providers.data.setRangeData(...)`.

Try it Out!

Add the History Plugin to your RevoGrid instance and experience seamless undo/redo functionality. This plugin is especially useful in applications that handle complex data operations, ensuring users can easily revert or reapply changes as needed.

<div hidden>

```javascript
// fixing render for multiframework
```

</div>
