Migration from 4.x

This guide helps you move an existing Redactor 4 integration to Redactor 5. It focuses on settings, the public API, and events — the surfaces most integrations and plugins use.

Redactor 5 keeps the same entry point (Redactor('#entry', options) → app), but several option names, method paths, and event names changed. Prefer the 5.x names in new code; where noted, older forms still work as aliases.

Related references:

What changed at a glance#

Area Redactor 4 Redactor 5
Content API Nested under app.editor.* Methods live directly on app
Undo stack app.state app.history
Low-level helpers app.create('selection') etc. app.getService('selection') etc.
Menu lists popups.* dropdown.*
Events config subscribe: { … } Prefer events: { … } (subscribe still works)
Event names Dotted (editor.load) Colon form in docs (editor:ready); dots remain a notation alias for the same segments
Plugins Object-literal plugins Class plugins (constructor / start / stop)
Premium add-ons Separate plugins Suites + expanded premium surface (Assistant, Review, Versions, …)

Settings#

Update constructor options first. Most options keep the same meaning; several were renamed, nested, or replaced.

Renamed or reshaped

Redactor 4 Redactor 5 Notes
clicktoedit clickToEdit CamelCase. Works on <div> hosts; ignored on <textarea>.
popups.format dropdown.format Format menu item list
popups.inline dropdown.inline Inline menu item list
popups.addbar dropdown.addbar “+” menu order
sync: false sync: { enabled: false } Boolean → object
syncDelay sync.debounce Same idea (ms)
state.limit history.limit Undo stack size
addPosition: 'top'\|'bottom' contentInsert: 'start'\|'end' Values renamed
clean.enterinline clean.enterInline Re-test behavior — docs/default differ from 4.x
link.target: '_blank' link.newTab: true UI / create-form default
link.size link.truncate Display truncation (default 30 → 24)
image.wrapWithStyle wrapWithStyle Top-level; may be true or an object
paste.stripAttr paste.sanitize Richer sanitize object
colorpicker colors Palette config (UI sizing knobs removed from docs)
autosave.interval (as primary delay) autosave.debounce Optional interval still exists for periodic saves
command: false — Use app.triggers.unregister('/') after init
// Redactor 4
Redactor('#entry', {
    clicktoedit: true,
    sync: false,
    syncDelay: 100,
    state: { limit: 100 },
    addPosition: 'bottom',
    popups: {
        format: ['text', 'h1', 'h2', 'h3']
    }
});

// Redactor 5
Redactor('#entry', {
    clickToEdit: true,
    sync: { enabled: false, debounce: 100 },
    history: { limit: 100 },
    contentInsert: 'end',
    dropdown: {
        format: ['text', 'h1', 'h2', 'h3']
    }
});

Removed from settings docs (verify in your build)

These were documented in 4.x and no longer have a dedicated settings page in 5.x. Do not assume runtime parity — replace or drop them deliberately:

classes, reorder, structure, grammarly, scrollOverflow, block.outline, paragraphizer, global drop (use image.drop), image.states, embed.script.

New options worth knowing

Option Role
ai AI Commands / backend integration
hostTabs Host panels next to the editor
themeStore Persist theme in localStorage
visualCaret Custom caret behavior
blockUid / trackedRefs Stable block ids / ref tracking
activeState Toolbar active-state rules
buttons.toolbar Explicit main toolbar button list
addbarItems Custom addbar entries at init

Plugins accept a string array as before, or a map with per-plugin options:

Redactor('#entry', {
    plugins: {
        counter: { plugin: CounterPlugin, options: { /* … */ } }
    }
});

See Plugins settings and AI settings.

Public API#

Calling patterns

Concern Redactor 4 Redactor 5
Content / focus app.editor.setContent(…) app.setContent(…)
Undo / redo app.state.undo() app.history.undo()
Services app.create('selection') app.getService('selection')
Insertion helper app.create('insertion') app.getService('inserter')
Options (plugins) this.opts.get('x') this.app.getOption('x')
Lang (plugins) this.lang.get('x') this.app.getLang('x')
Custom events this.app.broadcast('name', data) this.app.emit('name', data)
Containers app.container.get('toolbar') app.host.getToolbar() (and related getters)

Modules such as toolbar, block, and addbar still hang off app (app.toolbar.add(…)). You can also use app.getModule('toolbar') or app.invoke('toolbar.add', …).

Content and editor methods

Redactor 4 Redactor 5
app.editor.setContent({ html }) app.setContent(html)
app.editor.setJson(…) app.setJson(…)
app.editor.setEmpty() app.setEmpty()
app.editor.getContent() app.getHtml() or app.getContent('html')
app.editor.getJson() app.getJson()
app.editor.insertContent({ html }) app.insertHtml(html, options)
app.editor.insertContent({ instance }) app.insertBlock(block, options)
app.editor.setFocus(…) app.focusEditor(…)
app.editor.setBlur() app.blurEditor()
app.editor.hasFocus() app.hasFocus()
app.editor.isEmpty() app.isEditorEmpty()
app.editor.isSelectAll() app.isAllSelected()
app.editor.save() / restore() app.saveSelection() / restoreSelection()
app.disable() / enable() app.setEditorDisabled(true\|false)
app.readonly() / editable() app.setEditorReadonly(true\|false)
// Redactor 4
app.editor.setContent({ html: '<p>Hello</p>' });
const html = app.editor.getContent();
app.editor.insertContent({ html: '<p>More</p>' });
app.editor.setFocus('end');

// Redactor 5
app.setContent('<p>Hello</p>');
const html = app.getHtml();
app.insertHtml('<p>More</p>', { caret: 'end' });
app.focusEditor({ position: 'end' });

Modules and services

Redactor 4 Redactor 5
app.state.undo() / redo() app.history.undo() / redo()
app.source.open() / close() app.source.show() / hide()
app.source.is() app.source.isVisible()
app.source.getElement() app.source.getTextarea()
app.placeholder.set(…) app.placeholder.setEditorPlaceholder(…)
app.toolbar.unsetActive(…) app.toolbar.clearActive(…)
app.toolbar.disable / enable (by name) app.toolbar.setDisabled / clearDisabled
block.getBlock() (element) block.getEl()
app.create('selection') app.getService('selection')
app.create('insertion') app.getService('inserter')
app.create('caret') app.getService('caret')
app.create('offset') app.getService('offset')
app.create('utils') app.utils

Block focus APIs moved toward explicit focus methods:

// Redactor 4 (typical)
app.block.set(el, 'end');
app.block.unset();

// Redactor 5
app.block.focus(el, { caret: 'end' });
app.block.clearFocus();

The Dom wrapper (app.$doc, jQuery-like chain) is no longer part of the public API docs — work with native elements and services.

Full module and service lists: API overview.

Events#

Notation and config

Docs use colons: editor:ready. The legacy dotted form (editor.ready) is equivalent when the segments are the same — editor.ready ↔ editor:ready.

That is only a notation alias. It does not rename events that changed identity. For example, editor.load is not the same as editor:ready.

Redactor 4 Redactor 5
subscribe: { 'editor.load': … } Prefer events: { 'editor:ready': … }
this.app.broadcast('x', data) this.app.emit('x', data)
event.get('key') / event.set(…) Destructured payload; return values / stop() for hooks
// Redactor 4
Redactor('#entry', {
    subscribe: {
        'editor.load': function() {
            // …
        },
        'editor.keydown': function(event) {
            // …
        }
    }
});

// Redactor 5
Redactor('#entry', {
    events: {
        'editor:ready'() {
            // …
        },
        'key:down'({ event }) {
            // …
        }
    }
});

You can also subscribe after init with app.on / app.off — see Events overview.

Same name, new punctuation

These keep the same segments — switch dots to colons (or keep the dotted form as notation).

Redactor 4 Redactor 5
app.before.start app:before:start
app.start app:start
app.stop app:stop
editor.focus editor:focus
editor.blur editor:blur
editor.change editor:change
editor.empty editor:empty
editor.mousedown editor:mousedown
editor.mouseup editor:mouseup
block.before.remove block:before:remove
block.remove block:remove
image.upload image:upload
image.insert image:insert
image.change image:change
image.before.change image:before:change
image.upload.error image:upload:error
embed.insert embed:insert
link.change link:change
link.remove link:remove
upload.before.send upload:before:send
upload.error upload:error
autosave.before.send autosave:before:send
autosave.send autosave:send
autosave.error autosave:error
dropdown.before.open dropdown:before:open
dropdown.open dropdown:open
dropdown.close dropdown:close
form.before.create form:before:create
form.create form:create
ai.create ai:create
ai.before.send ai:before:send
ai.complete ai:complete
ai.before.insert ai:before:insert
ai.insert ai:insert
ai.discard ai:discard
ai.stop ai:stop
ai.error ai:error

Renamed events (update handlers)

Redactor 4 Redactor 5 Notes
editor.load editor:ready Editor started and ready
editor.insert inserter:insert Content inserted
editor.keydown key:down
editor.keyup key:up
editor.click click
editor.dragstart dnd:start
editor.dragover dnd:over
editor.dragleave dnd:leave
editor.drop dnd:drop
editor.before.copy clipboard:before:copy
editor.copy clipboard:copy (also clipboard:after:copy)
editor.before.cut clipboard:before:cut
editor.cut clipboard:cut (also clipboard:after:cut)
editor.before.paste / editor.paste clipboard:before:paste / clipboard:paste / clipboard:after:paste (+ cleaner:*:paste) Prefer clipboard + cleaner events
block.set block:focus Block selected / focused
block.unset block:focus:clear
block.change blocks:change Document-level change signal
inline.set inline:format
format.set blocks:format or inline:format Split by concern
link.add link:set
image.width image:dimensions
image.link / image.unlink link:set / link:remove (often with image scope)
upload.complete upload:success
source.open / source.close source:show / source:hide
toolbar.sticky / toolbar.static toolbar:sticky:start / toolbar:sticky:end
state.undo / state.redo history:restore Single restore hook (no separate undo/redo events)

For “document changed” listeners, prefer blocks:change in new code (you can still listen to editor:change).

Parse / output pipeline

HTML parse and unparse hooks were split into cleaner, parser, and output events. Map carefully and re-test:

Redactor 4 Start here in 5.x
editor.before.parse / editor.parse cleaner:before:input / cleaner:after:input, parser:before:html, parser:loaded
editor.before.unparse / editor.unparse output:prepare:html, output:before:html
editor.before.load Often parser:before:html or wait for editor:ready

Events without a direct replacement

No dedicated 5.x counterpart is documented for these 4.x events:

  • editor.mouseover
  • block.duplicate
  • list.item
  • embed.change
  • source.before.open
  • source.before.close
  • source.change
  • app.before.stop
  • link.click
  • image.click

For click-related cases, use the generic click event or focus events and filter by target or block type.

Plugins#

Rewrite object-literal plugins as classes with constructor(app, options), start(), and optional load() / stop().

// Redactor 4 (concept)
Redactor.add('plugin', 'hello', {
    start() {
        this.app.toolbar.add('hello', { title: 'Hi', command: 'hello.run' });
    },
    run() { /* … */ }
});

// Redactor 5
class HelloPlugin {
    constructor(app, options = {}) {
        this.app = app;
        this.options = options;
    }

    start() {
        this.app.toolbar.add('hello', {
            title: 'Hi',
            command: 'hello.run'
        });
    }

    run() {
        this.app.insertHtml('<p>Hello</p>', { caret: 'end' });
    }
}

Redactor('#entry', {
    plugins: ['hello'],
    // or: plugins: { hello: { plugin: HelloPlugin } }
});

Inside plugins, replace this.opts / this.lang / this.app.create(…) / broadcast with the 5.x patterns above. Full walkthrough: Create a plugin.

Suggested migration steps#

  1. Swap the Redactor 5 package (CSS/JS) and open the page — fix init errors first.
  2. Update settings renames (clickToEdit, dropdown, sync, history, …).
  3. Replace app.editor.* and app.state.* call sites.
  4. Replace app.create(…) with getService / app.utils.
  5. Move subscribe → events, update renamed event names (especially editor.load, keys, clipboard, DnD, block focus).
  6. Port custom plugins to classes; re-test toolbar commands and services.
  7. Re-check paste/clean/output hooks if you transform HTML on the way in or out.
  8. Optionally adopt new surfaces: Host tabs, JSON content, Premium features.

If something still fails after these steps, compare the old call with the matching page under Settings, API, or Events — method and payload shapes may have changed even when the feature still exists.