This is a Draft I made from notes I made a few month ago. It needs some testing and polishing.
How to Integrate CKEditor5 in a Directus Interface Extension
Directus embeds TinyMCE, which is pretty good. But what if you need CKEditor5 ?
This guide explains a way among others to create a custom Directus interface extension that embeds CKEditor5 with a custom CKEditor5 plugins.
A Directus interface is a type of field.
Prerequisites
- A running Directus project,
- Very basic understanding of Vue 3 and Directus extensions.
1 -
Generate a New Interface Extension
First, scaffold a new interface extension in your Directus project:
npx create-directus-extension@latest
This will create a folder like extensions/interfaces/ckeditor/
with a basic interface.vue
file.
source: directus doc: creating-extensions
2 -
Install Required Dependencies
Next, install CKEditor and its Vue integration into the extension subproject.
npm install --save ckeditor5 @ckeditor/ckeditor5-vue
3 - Create the CKEditor5 Interface
3.1 - Retrieve the CKEditor5 CSS
Unfortunately, I found no other way than importing manually the CSS file to load it.
So take it from their GitHub repo:
https://github.com/ckeditor/ckeditor5
Save it in your interface directory as ckeditor5.css
.
Sorry I forgot where to find this file. I’ll update that link as soon as I find it.
3.2 - Implement the Interface
Here’s a minimal version of the interface with basic CKEditor5 setup, excluding any custom plugins for now:
<!-- extensions/interfaces/ckeditor/interface.vue -->
<template>
<ckeditor
v-model="data"
:editor="editor"
:config="config"
@input="handleChange($event)"
/>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
import {
ClassicEditor,
Essentials,
Paragraph,
Bold,
Italic,
SourceEditing,
BlockQuote,
Table,
Link,
FontColor,
Underline,
Heading,
Image,
ImageInsert,
ImageToolbar,
ImageCaption,
ImageStyle,
ImageResize,
} from "ckeditor5";
import "./ckeditor5.css";
const props = defineProps({
value: {
type: String,
default: null,
},
});
const data = ref(props.value || "");
const emit = defineEmits(["input"]);
watch(
() => props.value,
(newValue) => {
data.value = newValue || "";
}
);
const editor = ClassicEditor;
const config = {
licenseKey: "GPL",
plugins: [
Essentials,
Paragraph,
Bold,
Italic,
SourceEditing,
BlockQuote,
Table,
Link,
FontColor,
Underline,
Heading,
Image,
ImageInsert,
ImageToolbar,
ImageCaption,
ImageStyle,
ImageResize,
],
toolbar: [
"undo",
"|",
"redo",
"bold",
"italic",
"underline",
"|",
"heading",
"blockQuote",
"insertTable",
"insertImageViaUrl",
"directusImageManager",
"link",
"|",
"fontColor",
"|",
"sourceEditing",
],
link: {
addTargetToExternalLinks: true,
defaultProtocol: "https://",
decorators: {
toggleDownloadable: {
mode: "manual",
label: "Downloadable",
attributes: {
download: "file",
},
},
},
},
image: {
toolbar: ["imageTextAlternative", "imageStyle:full", "imageStyle:side"],
},
};
function handleChange(newValue: string) {
emit("input", newValue);
}
</script>
3.3 - Configure CKEditor5 Like You Want
You can find all (or most) CKEditor5 official plugins in the ckeditor5
package.
In the config
object above, I’ve included a few commonly used ones as an example.
You can customize the plugins
and toolbar
arrays to suit your content needs.
4 -
Create a Custom CKEditor5 Plugin
What if you need something that none of the CKEditor can do ? Or you need to integrate one of your company’s custom CKEditor5 plugin ?
Well, you can create a plugin and import it.
4.1 - Create the Plugin
Create a new file like CKEditorPluginExample.ts
in the same directory or in a plugins/
subfolder.
// extensions/interfaces/ckeditor/plugins/CKEditorPluginExample.ts
import { Plugin, ButtonView } from "ckeditor5";
export default class CKEditorPluginExample extends Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add("CKEditorPluginExampleButton", (locale) => {
const view = new ButtonView(locale);
view.set({
label: "Directus CKEditor plugin example",
withText: true,
tooltip: true,
});
view.on("execute", () => {
console.log("Directus CKEditor button was clicked!");
});
return view;
});
}
}
This plugin doesn’t do much. It just adds a button that console.log some text. I hope it helps.
4.2 - Import the Plugin in Your Interface
In interface.vue
, import the plugin and register it like this:
import CKEditorPluginExample from "./plugins/CKEditorPluginExample";
...
const config = {
licenseKey: "GPL",
plugins: [
...
CKEditorPluginExample
],
toolbar: [
...
"CKEditorPluginExampleButton"
]
That’s it! Your CKEditor instance now includes your custom plugin with a custom button.
5 -
Run it
5.1 - Build your extension
npm run build
5.2 - Test in directus
Log into your directus instance and add a new text field.
You should find your new interface in the list of interfaces to choose from.