historyRedo

How historyRedo inputType performs redo operations and varies across browsers.

Overview

The historyRedo inputType is triggered when the user presses Ctrl/Cmd + Shift + Z (or Ctrl/Cmd + Y on some systems). The browser reapplies the last undone operation from its redo stack.

Basic Behavior

Scenario: Redo after undo

Current state (after undo)

HTML:
<p>Hello world</p>

After Ctrl/Cmd + Shift + Z (Redo)

HTML:
<p>Hello x world</p>
Previous undone operation reapplied

Browser Redo Stack

  • Browsers maintain a redo stack that stores undone operations
  • The redo stack is cleared when a new operation is performed after an undo
  • Redo stack depth and behavior varies between browsers

Editor Internal Model & DOM Synchronization

⚠️ Critical Exception: historyRedo May Not Fire

Similar to historyUndo: When editors use internal models and manipulate DOM directly, historyRedo event may not fire or may be ignored.

See historyUndo - Editor Internal Model & DOM Synchronization for detailed explanation of why this happens and how to handle it.

Editor-Specific Handling

Different editor frameworks maintain their own redo stacks, separate from the browser's redo stack. Here's how major editors implement historyRedo:

Slate.js

Redo Operation

Slate uses History.redo() to reapply undone operations:

    import { Editor, History } from 'slate';
import { withHistory } from 'slate-history';

const editor = withHistory(createEditor());

element.addEventListener('beforeinput', (e) => {
  if (e.inputType === 'historyRedo') {
    e.preventDefault();
    
    // Slate's History plugin handles redo
    History.redo(editor);
  }
});

function handleRedo() {
  History.redo(editor);
}
  
  • History.redo: Reapplies the last undone operation.
  • Operation-based: Reapplies operations from redo stack.
ProseMirror

Redo Operation

ProseMirror uses redo command:

    import { redo } from 'prosemirror-history';

view.dom.addEventListener('beforeinput', (e) => {
  if (e.inputType === 'historyRedo') {
    e.preventDefault();
    const { state, dispatch } = view;
    
    if (redo(state, dispatch)) {
      // Handled
    }
  }
});
  
  • redo command: Uses redo() command to reapply last undone transaction.
  • Transaction-based: Reapplies transactions from redo stack.
Draft.js

Redo Operation

Draft.js uses EditorState.redo() to reapply undone state:

    import { EditorState } from 'draft-js';

element.addEventListener('beforeinput', (e) => {
  if (e.inputType === 'historyRedo') {
    e.preventDefault();
    
    const redoStack = editorState.getRedoStack();
    if (!redoStack.isEmpty()) {
      const newState = EditorState.redo(editorState);
      setEditorState(newState);
    }
  }
});

function handleRedo() {
  const newState = EditorState.redo(editorState);
  setEditorState(newState);
}
  
  • EditorState.redo: Uses EditorState.redo() to reapply previous state.
  • State-based: Reapplies EditorState snapshots from redo stack.

Related resources