How to upgrade Tiptap v2 to v3
Tiptap v3 is a major update with breaking changes. This guide will help you upgrade your Tiptap v2 project to version 3.
Breaking Changes
Package Changes
- UMD builds have been removed (now using tsup)
Some packages may have been removed or renamed, so you may need to update your imports and dependencies. Here are some common changes:
Table packages
- import Table from '@tiptap/extension-table'
- import TableRow from '@tiptap/extension-table-row'
- import TableCell from '@tiptap/extension-table-cell'
- import TableHeader from '@tiptap/extension-table-header'
+ import { Table, TableRow, TableCell, TableHeader } from '@tiptap/extension-table'
List packages
- import BulletList from '@tiptap/extension-bullet-list'
- import OrderedList from '@tiptap/extension-ordered-list'
- import ListItem from '@tiptap/extension-list-item'
- import ListKeymap from '@tiptap/extension-list-keymap'
- import TaskList from '@tiptap/extension-task-list'
- import TaskItem from '@tiptap/extension-task-item'
+ import { BulletList, OrderedList, ListItem, ListKeymap, TaskList, TaskItem } from '@tiptap/extension-list'
Extensions
- import Focus from '@tiptap/extension-focus'
- import Placeholder from '@tiptap/extension-placeholder'
- import History from '@tiptap/extension-history'
- import Dropcursor from '@tiptap/extension-dropcursor'
- import Gapcursor from '@tiptap/extension-gapcursor'
- import CharacterCount from '@tiptap/extension-character-count'
+ import { Focus, Placeholder, UndoRedo, Dropcursor, Gapcursor, CharacterCount } from '@tiptap/extensions'
React Menus
- import { BubbleMenu, FloatingMenu } from '@tiptap/react'
+ import { BubbleMenu, FloatingMenu } from '@tiptap/react/menus'
Vue 3 Menus
- import { BubbleMenu, FloatingMenu } from '@tiptap/vue-3'
+ import { BubbleMenu, FloatingMenu } from '@tiptap/vue-3/menus'
Vue 2 Menus
- import { BubbleMenu, FloatingMenu } from '@tiptap/vue-2'
+ import { BubbleMenu, FloatingMenu } from '@tiptap/vue-2/menus'
Menu Systems
Tippy.js has been replaced with Floating UI for all floating elements. This affects:
- @tiptap/extension-bubble-menu
- @tiptap/extension-drag-handle
- @tiptap/extension-drag-handle-react
- @tiptap/extension-drag-handle-vue-2
- @tiptap/extension-drag-handle-vue-3
- @tiptap/extension-floating-menu
- @tiptap/extension-mention
- @tiptap/suggestion
Example migration:
// Before
import { BubbleMenu } from '@tiptap/react'
const menu = <BubbleMenu tippyOptions={{ duration: 100 }}>{/* menu content */}</BubbleMenu>
// After
import { BubbleMenu } from '@tiptap/react/menus'
import { offset } from '@floating-ui/dom'
const menu = (
<BubbleMenu
options={{
middleware: [offset(6)],
placement: 'top',
}}
>
{/* menu content */}
</BubbleMenu>
)
- New required dependency:
@floating-ui/dom
. Install it with:
npm install @floating-ui/dom@^1.6.0
Make sure to uninstall tippy.js
if you were using it, as it is no longer needed.
npm uninstall tippy.js
API Changes Text Style Updates
mergeNestedSpanStyles
now defaults totrue
- New text style extensions added:
font-size
background-color
line-height
TextStyleKit
API available for configuring multiple extensions
You can now use the new TextStyleKit
for easier configuration:
import { TextStyleKit } from '@tiptap/extension-text-style'
new Editor({
extensions: [
TextStyleKit.configure({
backgroundColor: {
types: ['textStyle'],
},
color: {
types: ['textStyle'],
},
fontFamily: {
types: ['textStyle'],
},
fontSize: {
types: ['textStyle'],
},
lineHeight: {
types: ['textStyle'],
},
}),
],
})
Introducing shouldRerenderOnTransaction
The @tiptap/react
Editor configuration now includes shouldRerenderOnTransaction
, controlling component rerendering.
Previously (pre-3.0.0), the Editor rerendered on every transaction-induced state update, facilitating continuous editor object tracking but potentially impacting rendering performance.
shouldRerenderOnTransaction
is disabled by default. Enable it by setting shouldRerenderOnTransaction: true
in the editor configuration.
Post-upgrade, UI elements relying on the Editor's state might become unresponsive. Solutions include disabling the option (potential performance implications) or implementing manual state tracking, as demonstrated:
function MyEditor() {
const [selection, setSelection] = useState({ from: 0, to: 0 });
const editor = useEditor({
// Editor configuration
onTransaction({ transaction }) {
setSelection({
from: transaction.selection.from,
to: transaction.selection.to,
});
},
});
}
You can also use useEditorState
to extract either the whole state or select specific values to extract.
const { currentSelection } = useEditorState({
editor,
selector: (snapshot) => {
return { currentSelection: snapshot.editor.state.selection }
},
})
This allows explicit state extraction and management for dependent UI logic.
Command Changes
clearContent
andsetContent
now emit updates by defaultsetContent
signature changed to (content, options)insertContent
behavior modified to prevent splitting text nodes at the beginning
NodeView Changes
The getPos
function in NodeViewRendererProps can now return undefined
. Update your code to handle this case:
// Before
const pos = nodeViewProps.getPos()
// After
const pos = nodeViewProps.getPos()
if (pos !== undefined) {
// use pos
}
Removed Features
editor.getCharacterCount()
method has been removedconsiderAnyAsEmpty
option removed from placeholder extension
New Features
Extensions Package
The new @tiptap/extensions
package combines multiple utility extensions:
This will automatically update the imports in your project.
Example migration:
- import CharacterCount from '@tiptap/extension-character-count'
+ import { CharacterCount } from '@tiptap/extensions'
Server-Side Rendering
Improved SSR support with the ability to run the editor on the server-side without rendering:
const editor = new Editor({
element: null, // opt-in to SSR
content: {
type: 'doc',
content: [
/* ... */
],
},
extensions: [
/* ... */
],
})
Mark Views
Adds support for mark views, which allow you to render custom views for marks within the editor. This is useful for rendering custom UI for marks, like a color picker for a text color mark or a link editor for a link mark.
Including support for React and Vue 3 frameworks
// Plain JS
import { Mark } from '@tiptap/core'
Mark.create({
// Other options...
addMarkView() {
return ({ mark, HTMLAttributes }) => {
const dom = document.createElement('b')
const contentDOM = document.createElement('span')
dom.appendChild(contentDOM)
return {
dom,
contentDOM,
}
}
},
})
// React
import { Mark } from '@tiptap/core'
import { ReactMarkViewRenderer } from '@tiptap/react'
Mark.create({
// ... other options
addMarkView() {
return ReactMarkViewRenderer(YourComponent)
},
})
// Vue 3
import { Mark } from '@tiptap/core'
import { VueMarkViewRenderer } from '@tiptap/vue-3'
Mark.create({
addMarkView() {
return VueMarkViewRenderer(Component)
},
})
Other Improvements
- New delete event tracking
- HTML parsing now uses happy-dom-without-node
- New node/mark attribute validation support
- Performance improvements for transaction handling
StarterKit Updates
StarterKit now includes these extensions by default:
If you've installed these separately, you can remove them:
- import Link from '@tiptap/extension-link'
- import ListKeymap from '@tiptap/extension-list-keymap'
- import Underline from '@tiptap/extension-underline'
Also make sure to update disabled extension names in your StarterKit configuration if you were using them:
const editor = new Editor({
extensions: [
StarterKit.configure({
- history: false, // disable history
+ undoRedo: false // disable undo/redo (previously history)
})
]
})
Migration Steps
-
Update Dependencies
- Remove UMD-dependent code
- Install
@floating-ui/dom
(for bubble & floating menus) - Update Tiptap packages to v3
-
Update Menu Implementations
- Replace
tippyOptions
with Floating UI options - Update menu imports to use
/menus
suffix
- Replace
-
Review NodeView Usage
- Add checks for
undefined
getPos
- Add checks for
-
Update Extensions
- Migrate to
@tiptap/extensions
where applicable - Remove redundant
StarterKit
extensions - Update text style configurations
- Migrate to
-
Review Command Usage
- Update
setContent
calls to use new signature - Check
clearContent
behavior with default updates
- Update