New Froala Editor v5.3.1 is here – Learn More
Froala Documentation
- Installation Guides
- Browser Support
- Languages Support
- Shortcuts
- Activation
- Examples
- Customize the Editor
- Use-cases
- Plugins
- APIs
- Development Frameworks
- Server Integrations
- Server SDKs
- Migration Guides
- Changelog
- Tutorials
- Froala Docs
- /
- Migration Guides
- /
- Migrate from Tiptap to Froala
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.
Whats on this page hide