History

PreviousNext

Understand the undo and redo batch shape stored by the history extension.

The history() extension tracks undo and redo batches for an editor.

Usage

import { createEditor } from "@platejs/plite";
import { history } from "@platejs/plite-history";
 
const editor = createEditor({
  extensions: [history()],
});
 
editor.read((state) => state.history.undos());
 
editor.update((tx) => {
  tx.history.undo();
});
 
editor.update({ history: "skip" }).text.insert("draft");
import { createEditor } from "@platejs/plite";
import { history } from "@platejs/plite-history";
 
const editor = createEditor({
  extensions: [history()],
});
 
editor.read((state) => state.history.undos());
 
editor.update((tx) => {
  tx.history.undo();
});
 
editor.update({ history: "skip" }).text.insert("draft");

History Object

import type { EditorStatePatch, Operation, Range } from "@platejs/plite";
 
export interface History {
  redos: Batch[];
  undos: Batch[];
}
 
interface Batch {
  operations: Operation[];
  selectionBefore: Range | null;
  selectionBeforeRoot?: string;
  statePatches: EditorStatePatch[];
}
import type { EditorStatePatch, Operation, Range } from "@platejs/plite";
 
export interface History {
  redos: Batch[];
  undos: Batch[];
}
 
interface Batch {
  operations: Operation[];
  selectionBefore: Range | null;
  selectionBeforeRoot?: string;
  statePatches: EditorStatePatch[];
}

Static Methods

History.isHistory(value: unknown): value is History

Returns true if the passed in value is a History object and acts as a type guard.

Editor API

state.history(): History

Read the current undo and redo stacks.

state.history.undos(): Batch[]

Read the undo stack.

state.history.redos(): Batch[]

Read the redo stack.

tx.history.undo(): void

Undo the previous history batch.

tx.history.redo(): void

Redo the next history batch.

tx.history.skip(): void

Do not save the current transaction to history.

tx.history.merge(): void

Merge the current transaction into the previous compatible undo batch.

tx.history.newBatch(): void

Start a fresh undo batch for the current transaction.

editor.update({ history: "skip" }, fn): void

Run one update without saving it to history.

editor.update({ history: "merge" }, fn): void

Run one update that merges into the previous compatible undo batch.

editor.update({ history: "new-batch" }, fn): void

Run one update where the first operation starts a fresh history batch, then the rest of the callback merges into that batch.

For one direct write, configure the update facade instead:

editor.update({ history: "skip" }).text.insert("draft");
editor.update({ history: "skip" }).text.insert("draft");

editor.update.history.undo(): void

Undo the previous history batch outside a larger transaction.

editor.update.history.redo(): void

Redo the next history batch outside a larger transaction.

Controlled Previews

Render proposed edits from local state, decorations, or sidecar UI until the user accepts them. Do not mutate document content for a preview and then try to make that preview history later.

Use a local defineStateField with persist: false and history: 'skip' for preview state. Cancel by clearing that field. Accept by clearing the preview and applying the real document edit in one normal update.

const previewReplacement = defineStateField<string | null>({
  key: "local.preview.replacement",
  history: "skip",
  initial: () => null,
  persist: false,
});
 
editor.update((tx) => {
  tx.setField(previewReplacement, null);
  tx.text.delete({ at: selectedRange });
  tx.text.insert(acceptedText);
});
const previewReplacement = defineStateField<string | null>({
  key: "local.preview.replacement",
  history: "skip",
  initial: () => null,
  persist: false,
});
 
editor.update((tx) => {
  tx.setField(previewReplacement, null);
  tx.text.delete({ at: selectedRange });
  tx.text.insert(acceptedText);
});

Undo then restores the document content without resurrecting preview UI.