The history() extension tracks undo and redo batches for an editor.
import { createEditor } from '@platejs/slate'
import { history } from '@platejs/slate-history'
const editor = createEditor({
extensions: [history()],
})
editor.read((state) => state.history.undos())
editor.update((tx) => {
tx.history.undo()
})
editor.api.history.withoutSaving(() => {
editor.update((tx) => {
tx.text.insert('draft')
})
})import { createEditor } from '@platejs/slate'
import { history } from '@platejs/slate-history'
const editor = createEditor({
extensions: [history()],
})
editor.read((state) => state.history.undos())
editor.update((tx) => {
tx.history.undo()
})
editor.api.history.withoutSaving(() => {
editor.update((tx) => {
tx.text.insert('draft')
})
})History Object
import type { EditorStatePatch, Operation, Range } from '@platejs/slate'
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/slate'
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.get(): 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.
editor.api.history.withMerging(fn: () => void): void
Run updates that merge into the previous history batch.
editor.api.history.withNewBatch(fn: () => void): void
Run updates where the first operation starts a new history batch.
editor.api.history.withoutMerging(fn: () => void): void
Run updates without merging the new operations into the previous batch.
editor.api.history.withoutSaving(fn: () => void): void
Run updates without saving operations to history.
editor.api.history.isMerging(): boolean | undefined
Read the current merge flag.
editor.api.history.isSaving(): boolean | undefined
Read the current saving flag.
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.
On This Page
History ObjectStatic MethodsHistory.isHistory(value: unknown): value is HistoryEditor APIstate.history.get(): Historystate.history.undos(): Batch[]state.history.redos(): Batch[]tx.history.undo(): voidtx.history.redo(): voideditor.api.history.withMerging(fn: () => void): voideditor.api.history.withNewBatch(fn: () => void): voideditor.api.history.withoutMerging(fn: () => void): voideditor.api.history.withoutSaving(fn: () => void): voideditor.api.history.isMerging(): boolean | undefinededitor.api.history.isSaving(): boolean | undefinedControlled Previews