Find out what's new in Tiptap V3

How to upgrade Tiptap v2 to v3

Editor

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'

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 to true
  • 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 and setContent now emit updates by default
  • setContent 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 removed
  • considerAnyAsEmpty 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

  1. Update Dependencies

    • Remove UMD-dependent code
    • Install @floating-ui/dom (for bubble & floating menus)
    • Update Tiptap packages to v3
  2. Update Menu Implementations

    • Replace tippyOptions with Floating UI options
    • Update menu imports to use /menus suffix
  3. Review NodeView Usage

    • Add checks for undefined getPos
  4. Update Extensions

    • Migrate to @tiptap/extensions where applicable
    • Remove redundant StarterKit extensions
    • Update text style configurations
  5. Review Command Usage

    • Update setContent calls to use new signature
    • Check clearContent behavior with default updates