Redactor is a plain JavaScript editor. In Vue 3 you wrap it in a component: create the editor when the component mounts, and call destroy() when the component is removed.
This page shows two complete examples — one editor and several editors on the same page.
This guide uses the ES module build (redactor.esm.min.js). Adjust paths to where you keep the package files.
Host element and Vue#
Redactor may move a div host into its own UI tree. If that div is also Vue’s component root, Vue loses the node and multiple editors (or v-if swaps) throw DOM errors like insertBefore.
Use a stable Vue root as an anchor, and create the real host div yourself in mounted. Vue only owns the anchor; Redactor owns the host:
template: '<div ref="anchor"></div>',
mounted() {
const host = document.createElement('div');
this.$refs.anchor.appendChild(host);
this.redactor = Redactor(host, config);
}
A textarea host also works (Redactor leaves it in place), but a div is fine with the anchor pattern above.
Single editor#
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Vue Redactor</title>
<link rel="stylesheet" href="/assets/redactor/redactor.css">
</head>
<body>
<div id="app">
<Redactor :config="configOptions"></Redactor>
{{ content }}
</div>
<script type="module">
import { createApp } from 'vue';
import Redactor from '/assets/redactor/redactor.esm.min.js';
import '/assets/redactor/plugins/counter.js';
const app = createApp({
data() {
return {
content: '',
configOptions: {
plugins: ['counter'],
content: '<p>Hello from Vue</p>'
}
};
}
});
app.component('Redactor', {
template: '<div ref="anchor"></div>',
props: {
config: {
type: Object,
default: () => ({
content: '<p></p>'
})
}
},
data() {
return {
redactor: null
};
},
mounted() {
this.init();
},
beforeUnmount() {
this.destroy();
},
methods: {
init() {
const config = {
...this.config,
events: {
...(this.config.events || {}),
'editor:change': ({ html }) => {
this.handleInput(html);
}
}
};
const host = document.createElement('div');
this.$refs.anchor.appendChild(host);
this.redactor = Redactor(host, config);
this.$parent.redactor = this.redactor;
},
destroy() {
if (this.redactor) {
this.redactor.destroy();
}
this.redactor = null;
this.$parent.redactor = null;
},
handleInput(html) {
this.$emit('input', html);
if (this.$parent) {
this.$parent.content = html;
}
}
}
});
app.mount('#app');
</script>
</body>
</html>
Notes:
- Always call
destroy()inbeforeUnmount(Vue 3) so Redactor can restore/remove its DOM before Vue tears down the anchor. - Call
destroy()on the Redactor instance (this.redactor), not on the DOM ref. - Document changes use the
editor:changeevent (viaevents/ legacysubscribein the config). See Events.
Multiple editors#
The same component can run several times. Pass different content and config per instance:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Vue Redactor</title>
<link rel="stylesheet" href="/assets/redactor/redactor.css">
</head>
<body>
<div id="app">
<Redactor :config="configOptions" :content="content1"></Redactor>
<br><br>
<Redactor :config="{ plugins: ['counter'] }" :content="content2"></Redactor>
</div>
<script type="module">
import { createApp } from 'vue';
import Redactor from '/assets/redactor/redactor.esm.min.js';
import '/assets/redactor/plugins/counter.js';
const app = createApp({
data() {
return {
configOptions: {
plugins: ['counter']
},
content1: '<p>Hello world 1!</p>',
content2: '<p>Hello world 2!</p>'
};
}
});
app.component('Redactor', {
template: '<div ref="anchor"></div>',
props: {
config: {
type: Object,
default: () => ({})
},
content: {
type: String,
default: ''
}
},
data() {
return {
redactor: null
};
},
mounted() {
this.init();
},
beforeUnmount() {
this.destroy();
},
methods: {
init() {
const config = {
...this.config,
content: this.content || this.config.content || '<p></p>',
events: {
...(this.config.events || {}),
'editor:change': ({ html }) => {
this.handleInput(html);
}
}
};
const host = document.createElement('div');
this.$refs.anchor.appendChild(host);
this.redactor = Redactor(host, config);
},
destroy() {
if (this.redactor) {
this.redactor.destroy();
}
this.redactor = null;
},
handleInput(html) {
this.$emit('input', html);
}
}
});
app.mount('#app');
</script>
</body>
</html>
Each instance owns its own anchor and Redactor app and cleans up in beforeUnmount.