Skip to content
Froala Documentation

Migrate from Tiptap to Froala

This guide maps the Tiptap v3 API to the Froala WYSIWYG Editor v5 API, one topic at a time. Every section pairs your current Tiptap code with the equivalent Froala code so you can move an existing integration over with confidence.

What you need to know first

Tiptap is a headless framework built on ProseMirror. It stores your content as a structured document (a ProseMirror node tree), ships no user interface, and expects you to assemble features from individual extensions and build your own toolbar. Froala takes the opposite approach: it is a batteries-included editor that works directly on HTML, renders a complete toolbar out of the box, and bundles 30+ plugins you switch on with a single option. You move from assembling an editor to configuring one.

Licensing also differs. Tiptap's core is open source under the MIT license, while advanced Pro extensions require a paid Tiptap Cloud subscription. Froala is a commercial product: you pass a purchased license key through the key option. Keep your Froala key handy before you start.

Concept Tiptap v3 Froala v5
Initialization new Editor({ element, extensions }) new FroalaEditor('#editor', options)
Element targeting A single DOM node via the element option CSS selector or DOM node — div, textarea, span, even an img
Enabling features Import and add extensions to the extensions array 30+ built-in pluginsEnabled plugins
Toolbar Headless — you build the UI yourself toolbarButtons array of button groups
Reading content editor.getHTML() editor.html.get()
Writing content editor.commands.setContent(html) editor.html.set(html)
Events onUpdate, onCreate options events: { contentChanged: … }
Teardown editor.destroy() editor.destroy()
Licensing MIT core; paid Pro extensions Commercial license key (key option)

The one mental shift: Tiptap hands you a document model and no UI, so you wire up extensions and render your own buttons. Froala hands you a working editor with a toolbar and plugins already attached, so your job is to choose what to show and set your options. Most of the code you wrote to build Tiptap's interface simply disappears.

Swap Tiptap for Froala in three steps

Remove the Tiptap packages, add Froala, and initialize on the same element. The container's existing HTML becomes your starting content.

Step 1: Remove Tiptap

Uninstall the Tiptap packages and delete the imports and the useEditor / new Editor setup from your component.

# Uninstall the Tiptap packages
npm uninstall @tiptap/core @tiptap/react @tiptap/pm @tiptap/starter-kit @tiptap/extension-image

// Delete the Tiptap imports and setup from your component
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

Step 2: Add Froala

The quickest path is the CDN. Include the packaged CSS and JS, which bundle the editor and every plugin.

<link href="https://cdn.jsdelivr.net/npm/froala-editor@latest/css/froala_editor.pkgd.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/froala-editor@latest/js/froala_editor.pkgd.min.js"></script>

// Or install from npm
npm install froala-editor

Step 3: Initialize the editor

Both editors mount onto a target element. Tiptap needs an explicit extension list; Froala loads its full feature set automatically, so you only pass your license key.

import FroalaEditor from 'froala-editor'

// The element's current HTML is used as the initial content
const editor = new FroalaEditor('#editor', {
  key: 'YOUR_FROALA_LICENSE_KEY',
})

Configuration and options

In Tiptap you configure behavior per extension and set editor-wide attributes through editorProps. In Froala you set every option in one flat options object.

Tiptap

const editor = new Editor({
  extensions: [
    StarterKit.configure({
      heading: { levels: [1, 2, 3] },
    }),
  ],
  editable: true,
  editorProps: {
    attributes: { class: 'prose focus:outline-none' },
  },
})

Froala

const editor = new FroalaEditor('#editor', {
  heightMin: 200,
  heightMax: 400,
  placeholderText: 'Start typing…',
  editorClass: 'prose',
  paragraphFormat: {
    N: 'Normal', H1: 'Heading 1',
    H2: 'Heading 2', H3: 'Heading 3',
  },
})

Froala exposes 240+ options covering sizing, HTML sanitization, paste behavior, and more. Browse the full list in the Options documentation.

Plugins

Tiptap features are extensions you import and add to the array. Froala features are plugins that are already loaded; you narrow them with pluginsEnabled or exclude a few with pluginsDisabled.

Tiptap

import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import TextAlign from '@tiptap/extension-text-align'

const editor = new Editor({
  extensions: [
    // Turn off a bundled feature
    StarterKit.configure({ undoRedo: false }),
    Image,
    TextAlign.configure({ types: ['heading', 'paragraph'] }),
  ],
})

Froala

// Load only the plugins you need
const editor = new FroalaEditor('#editor', {
  pluginsEnabled: ['align', 'image', 'link', 'lists', 'table'],
})

// Or load everything and exclude a few
const editor2 = new FroalaEditor('#editor', {
  pluginsDisabled: ['video', 'emoticons'],
})

pluginsDisabled takes priority over pluginsEnabled: a plugin listed in both is disabled. See the complete plugins list.

Getting and setting content

Both editors read and write HTML. Tiptap can also serialize its document to JSON; Froala works with HTML directly, which usually means less conversion when you persist content.

Tiptap

// Read
const html = editor.getHTML()
const json = editor.getJSON()

// Write (replaces the document)
editor.commands.setContent('<p>New content</p>')

// Insert at the cursor
editor.commands.insertContent('<strong>inline</strong>')

Froala

// Read
const html = editor.html.get()

// Write (replaces the document)
editor.html.set('<p>New content</p>')

// Insert at the cursor
editor.html.insert('<strong>inline</strong>')

Events

Tiptap accepts event callbacks at the top level of its constructor. Froala groups them under an events object, where this is the editor instance. Both let you bind and unbind listeners after creation.

Tiptap

const editor = new Editor({
  extensions: [StarterKit],
  onCreate({ editor }) { /* editor is ready */ },
  onUpdate({ editor }) {
    console.log(editor.getHTML())
  },
  onFocus({ editor }) {},
  onBlur({ editor }) {},
})

// Bind / unbind later
editor.on('update', () => console.log('changed'))

Froala

const editor = new FroalaEditor('#editor', {
  events: {
    initialized() { /* editor is ready; this = editor */ },
    contentChanged() {
      console.log(this.html.get())
    },
    focus() {},
    blur() {},
  },
})

// Bind / unbind later
editor.events.on('contentChanged', function () {
  console.log(this.html.get())
})

See the full events reference for the 100+ events Froala fires.

Methods

Tiptap changes state through chained commands and reads state through methods. Froala exposes command methods and namespaced helpers. Note the different read-only calls: Tiptap uses setEditable, Froala uses edit.on / edit.off.

Tiptap

// Formatting via chained commands
editor.chain().focus().toggleBold().run()

// Read-only mode
editor.setEditable(false)

// Focus and blur
editor.commands.focus()
editor.commands.blur()

// State checks
editor.isActive('bold')
editor.isEditable

// Teardown
editor.destroy()

Froala

// Formatting via command methods
editor.commands.bold()

// Read-only mode
editor.edit.off()
editor.edit.on()

// Focus and blur
editor.events.focus()
editor.events.disableBlur()

// State checks
editor.format.is('bold')

// Teardown
editor.destroy()

Froala ships 220+ methods; browse them in the methods documentation.

Image upload

This is where the two approaches diverge most. Tiptap's Image extension only renders an image node; you write the upload logic yourself and insert the returned URL. Froala includes a full uploader driven entirely by options.

Tiptap

import Image from '@tiptap/extension-image'

const editor = new Editor({
  extensions: [StarterKit, Image],
})

// You implement the upload and insert the URL yourself
async function uploadAndInsert(file) {
  const body = new FormData()
  body.append('file', file)
  const res = await fetch('/upload_image', { method: 'POST', body })
  const { link } = await res.json()
  editor.chain().focus().setImage({ src: link }).run()
}

Froala

// Upload, validation, and insertion are built in
const editor = new FroalaEditor('#editor', {
  imageUploadURL: '/upload_image',
  imageUploadParam: 'file',
  imageUploadMethod: 'POST',
  imageMaxSize: 5 * 1024 * 1024,
  imageAllowedTypes: ['jpeg', 'jpg', 'png'],
  events: {
    'image.uploaded'(response) {
      // Image uploaded to the server
    },
    'image.error'(error, response) {
      // Handle upload errors
    },
  },
})

Froala's uploader handles drag-and-drop, paste, progress, and server responses for you. Read the image upload guide for the full set of parameters and events.

Framework integration — React

Tiptap's React binding gives you the useEditor hook and an <EditorContent> renderer. Froala's react-froala-wysiwyg package gives you a single component with two-way binding through model and onModelChange.

Tiptap

// npm install @tiptap/react @tiptap/pm @tiptap/starter-kit
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Hello World!</p>',
    immediatelyRender: false, // avoid SSR hydration errors
    onUpdate: ({ editor }) => console.log(editor.getHTML()),
  })

  return <EditorContent editor={editor} />
}

Froala

// npm install react-froala-wysiwyg froala-editor
import { useState } from 'react'
import 'froala-editor/css/froala_style.min.css'
import 'froala-editor/css/froala_editor.pkgd.min.css'
import FroalaEditorComponent from 'react-froala-wysiwyg'

function MyEditor() {
  const [model, setModel] = useState('<p>Hello World!</p>')

  return (
    <FroalaEditorComponent
      tag="textarea"
      model={model}
      onModelChange={setModel}
      config={{ key: 'YOUR_FROALA_LICENSE_KEY' }}
    />
  )
}

Froala also provides official SDKs for Angular and Vue that follow the same config / model pattern.

Feature mapping reference

Use this table to find the Froala equivalent of the Tiptap extension you rely on today. Where Tiptap requires a separate install, Froala's feature is already present as a plugin.

Feature Tiptap v3 Froala v5
Bold / italic / underline StarterKit marks (Bold, Italic, Underline) Built-in bold, italic, underline commands
Headings Heading (in StarterKit) paragraphFormat option
Lists BulletList / OrderedList (in StarterKit) lists plugin, formatUL / formatOL
Links Link (in StarterKit) link plugin, insertLink
Images @tiptap/extension-image (no upload) image plugin (upload built in)
Tables @tiptap/extension-table (+ related) table plugin
Text alignment @tiptap/extension-text-align align plugin
Code block CodeBlock (in StarterKit) codeView / codeBeautifier
Placeholder @tiptap/extension-placeholder placeholderText option
Character count @tiptap/extension-character-count charCounter plugin
Undo / redo UndoRedo (in StarterKit) Built-in undo / redo
Markdown Markdown extension markdown plugin
Toolbar UI None — build your own toolbarButtons (built in)

You're ready to ship

You have mapped every core Tiptap API to its Froala equivalent. With the toolbar, plugins, and image uploader already built in, most of your custom UI code can go. Explore these references to fine-tune your setup.

Do you think we can improve this article? Let us know.

Ready to dive in? Explore our plans