Tiptap Editor 3.0 Beta is out. Start here

Migrate from CKEditor 5 to Tiptap

Editor

If you're moving away from CKEditor, Tiptap is a great alternative.Migrating from CKEditor 5 to Tiptap is straightforward. This guide covers everything you need to know for a smooth transition.

Content migration

HTML content compatibility

CKEditor 5 typically outputs HTML content, which Tiptap can use directly without any conversion:

// Your existing CKEditor 5 content
const existingContent =
  '<p>Hello <strong>world</strong>!</p><ul><li>Item 1</li><li>Item 2</li></ul>'

// Use directly in Tiptap
const editor = new Editor({
  content: existingContent,
  extensions: [StarterKit],
})

While HTML works perfectly, we recommend converting it to Tiptap's JSON format for better performance and readability. For batch conversion of existing content, use the HTML utility to convert HTML to JSON programmatically.

Editor setup

Installation

First, install Tiptap and its dependencies:

npm install @tiptap/core @tiptap/starter-kit

Tiptap supports all modern frontend UI frameworks like React and Vue. Follow the framework-specific installation instructions in our installation guides.

Basic editor setup

Replace your CKEditor 5 initialization with Tiptap:

// CKEditor 5 (before)
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials'
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold'
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic'

ClassicEditor.create(document.querySelector('#editor'), {
  plugins: [Essentials, Bold, Italic],
  toolbar: ['bold', 'italic'],
})

// Tiptap (after)
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'

const editor = new Editor({
  element: document.querySelector('#editor'),
  extensions: [StarterKit],
  content: '<p>Hello World!</p>',
})

Extensions

Understanding Tiptap's extension system

Tiptap uses a modular extension system that resembles CKEditor 5's plugin architecture. Each feature is an independent extension that can be configured and customized.

The StarterKit is a bundle of all the basic extensions, and you can add or remove other extensions as needed.

Explore all available extensions in our extensions guide, or create your own to support custom functionality and HTML elements.

Common CKEditor 5 plugin equivalents

CKEditor 5 PluginTiptap ExtensionNotes
BoldBoldIncluded in StarterKit
ItalicItalicIncluded in StarterKit
ListBulletList, OrderedList, ListItemIncluded in StarterKit
LinkLinkIncluded in StarterKit
ImageImageAvailable separately
TableTableAvailable separately
CodeBlockCodeBlockIncluded in StarterKit
HeadingHeadingIncluded in StarterKit
BlockquoteBlockquoteIncluded in StarterKit
AlignmentTextAlignAvailable separately
FontColorTextStyle, ColorAvailable separately
FontSizeTextStyle, FontSizeAvailable separately

Extension configuration

import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import Table from '@tiptap/extension-table'
import TableRow from '@tiptap/extension-table-row'
import TableHeader from '@tiptap/extension-table-header'
import TableCell from '@tiptap/extension-table-cell'
import TextAlign from '@tiptap/extension-text-align'

const editor = new Editor({
  extensions: [
    StarterKit,
    Image.configure({
      inline: true,
      allowBase64: true,
    }),
    Table.configure({
      resizable: true,
    }),
    TableRow,
    TableHeader,
    TableCell,
    TextAlign.configure({
      types: ['heading', 'paragraph'],
    }),
  ],
})

Custom extensions

For CKEditor 5 custom plugins, create custom Tiptap extensions. See our custom extensions guide for detailed instructions.

UI implementation

Toolbar implementation

CKEditor 5's toolbar configuration translates to custom UI components in Tiptap:

// CKEditor 5 toolbar config
toolbar: ['heading', 'bold', 'italic', 'link', 'bulletedList', 'numberedList']

// Tiptap equivalent (React example)
function Toolbar({ editor }) {
  if (!editor) return null

  return (
    <div className="toolbar">
      <select
        onChange={(e) => {
          const level = parseInt(e.target.value)
          if (level === 0) {
            editor.chain().focus().setParagraph().run()
          } else {
            editor.chain().focus().toggleHeading({ level }).run()
          }
        }}
        value={
          editor.isActive('heading', { level: 1 })
            ? 1
            : editor.isActive('heading', { level: 2 })
              ? 2
              : editor.isActive('heading', { level: 3 })
                ? 3
                : 0
        }
      >
        <option value={0}>Paragraph</option>
        <option value={1}>Heading 1</option>
        <option value={2}>Heading 2</option>
        <option value={3}>Heading 3</option>
      </select>

      <button
        onClick={() => editor.chain().focus().toggleBold().run()}
        className={editor.isActive('bold') ? 'active' : ''}
      >
        Bold
      </button>

      <button
        onClick={() => editor.chain().focus().toggleItalic().run()}
        className={editor.isActive('italic') ? 'active' : ''}
      >
        Italic
      </button>

      <button
        onClick={() => {
          const url = window.prompt('URL')
          if (url) {
            editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
          }
        }}
        className={editor.isActive('link') ? 'active' : ''}
      >
        Link
      </button>

      <button
        onClick={() => editor.chain().focus().toggleBulletList().run()}
        className={editor.isActive('bulletList') ? 'active' : ''}
      >
        Bullet List
      </button>

      <button
        onClick={() => editor.chain().focus().toggleOrderedList().run()}
        className={editor.isActive('orderedList') ? 'active' : ''}
      >
        Numbered List
      </button>
    </div>
  )
}

Pre-built UI components

For faster development, use Tiptap's pre-built UI components:

Balloon editor equivalent

CKEditor 5's balloon editor can be replicated using Tiptap's BubbleMenu:

import { BubbleMenu } from '@tiptap/react'

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
  })

  return (
    <>
      <EditorContent editor={editor} />
      <BubbleMenu editor={editor}>
        <button
          onClick={() => editor.chain().focus().toggleBold().run()}
          className={editor.isActive('bold') ? 'active' : ''}
        >
          Bold
        </button>
        <button
          onClick={() => editor.chain().focus().toggleItalic().run()}
          className={editor.isActive('italic') ? 'active' : ''}
        >
          Italic
        </button>
        <button
          onClick={() => {
            const url = window.prompt('URL')
            if (url) {
              editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
            }
          }}
          className={editor.isActive('link') ? 'active' : ''}
        >
          Link
        </button>
      </BubbleMenu>
    </>
  )
}

Migration checklist

Next steps