New Froala Editor v5.2.0 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
- /
- CKEditor 5
Migrate from CKEditor 5 to Froala
This guide walks you through replacing CKEditor 5 with Froala Editor. Each section maps a CKEditor 5 concept directly to its Froala equivalent, with code examples so you can update your project quickly and confidently.
What you need to know first
Before you touch any code, it helps to understand how the two editors differ structurally. The table below maps the core concepts.
| Concept | CKEditor 5 | Froala |
|---|---|---|
| Create the editor | ClassicEditor.create(el, config) → Promise | new FroalaEditor(selector, options, cb) |
| Target element | DOM node via document.querySelector | CSS selector string or DOM node |
| Enable features | Import plugins, list in plugins array | Bundled by default; toggle with pluginsEnabled |
| Toolbar | toolbar array of named items | toolbarButtons array (with responsive grouping) |
| Read content | editor.getData() | editor.html.get() |
| Write content | editor.setData(html) | editor.html.set(html) |
| Listen to changes | editor.model.document.on('change:data', cb) | events: { contentChanged: cb } |
| Tear down | editor.destroy() → Promise | editor.destroy() |
| Licensing | licenseKey ('GPL' or commercial key) | key (commercial activation key) |
Key mental shift: Froala is synchronous and configuration-driven. You do not assemble a plugin list to get bold, lists, links, images, or tables — they are already bundled. Most of your migration effort goes into translating configuration, not wiring up modules.
Getting Started
Step 1 — Remove CKEditor 5
Remove CKEditor 5 and its dependencies from your project.
Remove via npm
npm uninstall Remove via CDN
Remove the CKEditor 5 stylesheet and script from your HTML:
<!-- Remove these -->
<link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/48.2.0/ckeditor5.css" />
<script src="https://cdn.ckeditor.com/ckeditor5/48.2.0/ckeditor5.umd.js"></script>
Also remove any ClassicEditor.create(…) initialization code — you will replace it in Step 3.
Step 2 — Add Froala
Option A — CDN
Add the stylesheet to your <head>:
<link href="https://cdn.jsdelivr.net/npm/froala-editor/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
Add the script before the closing </body> tag:
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/froala-editor/js/froala_editor.pkgd.min.js"></script>
The .pkgd ("packaged") bundle includes every plugin — the closest match to a fully featured CKEditor 5 build.
Option B — npm
npm install froala-editor --save
import FroalaEditor from 'froala-editor';
import 'froala-editor/css/froala_editor.pkgd.min.css';
import 'froala-editor/css/froala_style.min.css';
// Or import only the plugins you need:
import 'froala-editor/js/plugins/align.min.js';
import 'froala-editor/js/plugins/lists.min.js';
import 'froala-editor/js/plugins/image.min.js';
Step 3 — Initialize the editor
CKEditor 5 creates an instance asynchronously and resolves a Promise. Froala creates the instance synchronously and accepts an optional callback that runs once initialization finishes.
import { ClassicEditor, Essentials,
Bold, Italic, Paragraph } from 'ckeditor5';
import 'ckeditor5/ckeditor5.css';
ClassicEditor
.create(document.querySelector('#editor'), {
licenseKey: 'GPL',
plugins: [ Essentials, Bold, Italic, Paragraph ],
toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ]
})
.then(editor => { console.log('Ready.', editor); })
.catch(err => console.error(err));
const editor = new FroalaEditor(
'#editor',
{
toolbarButtons: [ 'undo', 'redo', '|', 'bold', 'italic' ]
},
function () {
// Runs once initialized — equivalent to .then()
console.log('Ready.', this);
}
);
Key differences: Froala accepts a
'#editor' CSS selector string directly — no document.querySelector needed. The third argument is an init callback (no Promise). There is no plugin list for bold, italic, or basic editing — those are bundled.Complete HTML page
<!doctype html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/froala-editor/css/froala_editor.pkgd.min.css"
rel="stylesheet" />
</head>
<body>
<div id="editor"></div>
<script src="https://cdn.jsdelivr.net/npm/froala-editor/js/froala_editor.pkgd.min.js"></script>
<script>
new FroalaEditor('#editor', {
toolbarButtons: ['bold', 'italic', 'underline', '|', 'insertLink']
});
</script>
</body>
</html>
Configuration and options
Toolbar
Both editors define the toolbar as an ordered array of named buttons and use '|' as a separator. Button names differ, so the migration is mostly a find-and-replace.
| Action | CKEditor 5 item | Froala button |
|---|---|---|
| Bold | bold | bold |
| Italic | italic | italic |
| Underline | underline | underline |
| Headings | heading | paragraphFormat |
| Bulleted list | bulletedList | formatUL |
| Numbered list | numberedList | formatOL |
| Link | link | insertLink |
| Image | imageUpload | insertImage |
| Table | insertTable | insertTable |
| Block quote | blockQuote | quote |
| Text alignment | alignment | alignLeft, alignCenter, alignRight |
| Font color | fontColor | textColor |
| Remove formatting | removeFormat | clearFormatting |
| Undo / Redo | undo / redo | undo / redo |
toolbar: [
'heading', '|',
'bold', 'italic',
'link', '|',
'bulletedList',
'numberedList',
'insertTable'
]
toolbarButtons: [
'paragraphFormat', '|',
'bold', 'italic',
'insertLink', '|',
'formatUL',
'formatOL',
'insertTable'
]
Responsive toolbars (Froala bonus): Froala can serve different toolbars per screen size usingtoolbarButtonsMD,toolbarButtonsSM, andtoolbarButtonsXSoptions. CKEditor 5 does not support this out of the box.
Common options mapping
ClassicEditor.create(el, {
licenseKey: 'GPL',
placeholder: 'Write something…',
language: 'fr',
initialData: '<p>Welcome!</p>',
});
new FroalaEditor('#editor', {
key: 'YOUR_LICENSE_KEY',
placeholderText: 'Write something…',
language: 'fr',
// Set initial content via element HTML
// or editor.html.set() after init
});
Plugins
In CKEditor 5, a feature exists only if you import its plugin and declare it in the plugins array. In Froala, the packaged build already includes all plugins. Control which are active with the pluginsEnabled option.
import {
ClassicEditor,
Essentials,
Bold,
Italic,
Link,
List,
Image,
Table,
Paragraph
} from 'ckeditor5';
ClassicEditor.create(el, {
licenseKey: 'GPL',
plugins: [
Essentials,
Bold,
Italic,
Link,
List,
Image,
Table,
Paragraph
]
});
// .pkgd bundle: all plugins already included.
// Just declare what you want active:
new FroalaEditor('#editor', {
pluginsEnabled: [
'link', 'lists',
'image', 'table'
],
toolbarButtons: [
'bold', 'italic',
'insertLink', 'formatUL',
'insertImage', 'insertTable'
]
});
Getting and setting content
// Get content
const html = editor.getData();
// Set content
editor.setData('<p>New content</p>');
// Get content
const html = editor.html.get();
// Set content
editor.html.set('<p>New content</p>');
// Insert at cursor position
editor.html.insert('<b>Inserted</b>');
Rendering content outside the editor: Wrap saved HTML in an element with thefr-viewclass and includefroala_style.min.cssto preserve all formatting styles.
Events
CKEditor 5 exposes events through several observable objects. Froala centralizes all events in a single events config object. Inside a Froala event handler, this refers to the editor instance.
ClassicEditor
.create(el, { licenseKey: 'GPL' })
.then(editor => {
editor.model.document.on(
'change:data',
() => console.log(editor.getData())
);
editor.editing.view.document
.on(
'focus',
() => console.log('focused')
);
editor.editing.view.document
.on(
'blur',
() => console.log('blurred')
);
});
new FroalaEditor('#editor', {
events: {
initialized: function () {
console.log('ready');
},
contentChanged: function () {
console.log(this.html.get());
},
focus: function () {
console.log('focused');
},
blur: function () {
console.log('blurred');
}
}
});
Key difference: CKEditor 5 attaches events via
editor.on() calls on multiple observable objects inside a .then() callback. Froala accepts a single events object directly in the config. Inside Froala event callbacks, this is the editor instance — giving you direct API access.Methods
Read-only mode
// Lock-based read-only
editor.enableReadOnlyMode('my-lock');
editor.disableReadOnlyMode('my-lock');
// Direct toggle
editor.edit.off(); // make read-only
editor.edit.on(); // make editable again
Focus and destroy
// Focus
editor.focus();
// Destroy (returns a Promise)
editor.destroy()
.then(() => console.log('destroyed'));
// Focus
editor.events.focus();
// Destroy (synchronous)
editor.destroy();
Image upload
ClassicEditor.create(el, {
licenseKey: 'GPL',
simpleUpload: {
uploadUrl: '/upload',
headers: {
Authorization: 'Bearer <token>'
}
}
});
new FroalaEditor('#editor', {
imageUploadURL: '/upload',
imageUploadMethod: 'POST',
imageMaxSize: 5 * 1024 * 1024,
imageAllowedTypes: [
'jpeg',
'jpg',
'png',
'gif'
],
events: {
'image.error': function (err) {
console.error(err);
}
}
});
Server response format: Froala handles the upload form automatically. Your server endpoint should return { "link": "/path/to/image.jpg" }. See the image upload guide for server-side SDK examples. Framework integration — React
import { CKEditor } from '@ckeditor/ckeditor5-react';
import {
ClassicEditor,
Essentials,
Bold,
Italic,
Paragraph
} from 'ckeditor5';
import 'ckeditor5/ckeditor5.css';
function MyEditor() {
return (
<CKEditor
editor={ClassicEditor}
config={{
licenseKey: 'GPL',
plugins: [Essentials, Bold, Italic, Paragraph],
toolbar: ['bold', 'italic']
}}
data="<p>Hello world!</p>"
onChange={(e, editor) => console.log(editor.getData())}
/>
);
}
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={{ toolbarButtons: ['bold', 'italic'] }}
/>
);
}
npm install react-froala-wysiwyg froala-editor
Feature mapping reference
A quick lookup for the most common APIs you will translate.
| What you want | CKEditor 5 | Froala |
|---|---|---|
| Create | ClassicEditor.create(el, config) | new FroalaEditor(selector, options, cb) |
| On ready | .then(editor => …) | init callback or events.initialized |
| Get HTML | editor.getData() | editor.html.get() |
| Set HTML | editor.setData(html) | editor.html.set(html) |
| Insert HTML | model writer API | editor.html.insert(html) |
| Toolbar | toolbar: [...] | toolbarButtons: [...] |
| Placeholder | placeholder: '...' | placeholderText: '...' |
| Language | language: 'de' | language: 'de' |
| Read-only on | enableReadOnlyMode(id) | editor.edit.off() |
| Read-only off | disableReadOnlyMode(id) | editor.edit.on() |
| Focus | editor.focus() | editor.events.focus() |
| Destroy | editor.destroy() → Promise | editor.destroy() |
| Content change | model.document.on('change:data', cb) | events: { contentChanged: cb } |
| Image upload URL | simpleUpload.uploadUrl | imageUploadURL |
| License | licenseKey: 'GPL' | key: 'YOUR_KEY' |
Next steps
You now have a working Froala editor that mirrors your CKEditor 5 setup. Explore these resources to go further.
Do you think we can improve this article? Let us know.
Whats on this page hide