The History extension (also known as UndoRedo) provides undo and redo functionality for your editor. It tracks changes and allows users to step backward and forward through their edit history.
If you’re using the @tiptap/extension-collaboration package, do not use this extension. The Collaboration extension includes its own history implementation that’s designed to work in collaborative environments.
Installation
The History extension is included in the @tiptap/extensions package and is also part of the StarterKit.
npm install @tiptap/extensions
Basic Usage
Included in StarterKit
The History extension is included by default when using StarterKit:
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
const editor = new Editor({
extensions: [
StarterKit, // History is included
],
})
Standalone Usage
import { Editor } from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { UndoRedo } from '@tiptap/extensions/undo-redo'
const editor = new Editor({
extensions: [
Document,
Paragraph,
Text,
UndoRedo,
],
})
Custom Configuration
import StarterKit from '@tiptap/starter-kit'
const editor = new Editor({
extensions: [
StarterKit.configure({
history: {
depth: 50,
newGroupDelay: 1000,
},
}),
],
})
Configuration Options
The amount of history events that are collected before the oldest events are discarded. This prevents the history from growing indefinitely.Default: 100UndoRedo.configure({
depth: 50, // Keep only last 50 changes
})
The delay in milliseconds between changes after which a new history group should be started. Changes that occur within this delay are grouped together as a single undo/redo step.Default: 500UndoRedo.configure({
newGroupDelay: 1000, // Group changes within 1 second
})
Commands
Undo recent changes.Keyboard shortcuts: Cmd/Ctrl + Z, Cmd/Ctrl + я (Russian layout)
Reapply reverted changes.Keyboard shortcuts:
Cmd/Ctrl + Shift + Z
Cmd/Ctrl + Y
Cmd/Ctrl + Shift + я (Russian layout)
Keyboard Shortcuts
| Shortcut | Command | Description |
|---|
Cmd/Ctrl + Z | undo | Undo the last change |
Cmd/Ctrl + Shift + Z | redo | Redo the last undone change |
Cmd/Ctrl + Y | redo | Redo the last undone change |
Cmd/Ctrl + я | undo | Undo (Russian keyboard layout) |
Cmd/Ctrl + Shift + я | redo | Redo (Russian keyboard layout) |
Advanced Examples
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
const editor = new Editor({
extensions: [StarterKit],
})
// Create undo button
const undoButton = document.querySelector('#undo')
undoButton.addEventListener('click', () => {
editor.commands.undo()
})
// Create redo button
const redoButton = document.querySelector('#redo')
redoButton.addEventListener('click', () => {
editor.commands.redo()
})
// Update button states
editor.on('transaction', () => {
undoButton.disabled = !editor.can().undo()
redoButton.disabled = !editor.can().redo()
})
React Component with Undo/Redo
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { useState, useEffect } from 'react'
function Editor() {
const editor = useEditor({
extensions: [StarterKit],
content: '<p>Start typing...</p>',
})
const [canUndo, setCanUndo] = useState(false)
const [canRedo, setCanRedo] = useState(false)
useEffect(() => {
if (!editor) return
const updateHistoryState = () => {
setCanUndo(editor.can().undo())
setCanRedo(editor.can().redo())
}
editor.on('transaction', updateHistoryState)
updateHistoryState()
return () => {
editor.off('transaction', updateHistoryState)
}
}, [editor])
return (
<div>
<div className="toolbar">
<button
onClick={() => editor.commands.undo()}
disabled={!canUndo}
>
Undo
</button>
<button
onClick={() => editor.commands.redo()}
disabled={!canRedo}
>
Redo
</button>
</div>
<EditorContent editor={editor} />
</div>
)
}
Shorter History Depth for Memory Efficiency
const editor = new Editor({
extensions: [
StarterKit.configure({
history: {
depth: 20, // Keep only 20 steps instead of default 100
},
}),
],
})
Faster Grouping for Rapid Edits
// Useful for live coding editors or fast-paced note-taking
const editor = new Editor({
extensions: [
StarterKit.configure({
history: {
newGroupDelay: 200, // Group changes within 200ms
},
}),
],
})
Disable History in StarterKit
If you want to use the Collaboration extension instead:
import StarterKit from '@tiptap/starter-kit'
import Collaboration from '@tiptap/extension-collaboration'
import * as Y from 'yjs'
const ydoc = new Y.Doc()
const editor = new Editor({
extensions: [
StarterKit.configure({
history: false, // Disable default history
}),
Collaboration.configure({
document: ydoc,
}),
],
})
Check if Undo/Redo is Available
// Check if undo is possible
if (editor.can().undo()) {
console.log('Undo is available')
editor.commands.undo()
}
// Check if redo is possible
if (editor.can().redo()) {
console.log('Redo is available')
editor.commands.redo()
}
Vue Component Example
<template>
<div>
<div class="toolbar">
<button @click="undo" :disabled="!canUndo">
Undo
</button>
<button @click="redo" :disabled="!canRedo">
Redo
</button>
</div>
<editor-content :editor="editor" />
</div>
</template>
<script>
import { Editor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
canUndo: false,
canRedo: false,
}
},
mounted() {
this.editor = new Editor({
extensions: [StarterKit],
content: '<p>Start typing...</p>',
onTransaction: () => {
this.canUndo = this.editor.can().undo()
this.canRedo = this.editor.can().redo()
},
})
},
beforeUnmount() {
this.editor.destroy()
},
methods: {
undo() {
this.editor.commands.undo()
},
redo() {
this.editor.commands.redo()
},
},
}
</script>
How History Works
The History extension groups changes based on time. When you type continuously, all changes within the newGroupDelay window are grouped together as a single undo step. This creates a natural editing experience:
- Typing continuously: All text entered within 500ms is one undo step
- Pausing then typing: Creates a new undo step
- Each formatting change: Usually a separate undo step
- Large operations: Typically a single undo step
Source Code
View the source code on GitHub: