How to Integrate CKEditor5 in a Directus Interface Extension

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.

:hammer_and_wrench: Prerequisites

  • A running Directus project,
  • Very basic understanding of Vue 3 and Directus extensions.

1 - :brick: 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 - :package: 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:

:package: 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. :sweat_smile:


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 - :page_with_curl: 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. :slight_smile:


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 - :test_tube: 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.

1 Like

Thank you for sharing this Kian. This is the first guide of it’s kind that’s been added to the forum :smiling_face_with_tear: and that means you extra rock! :rock::sign_of_the_horns:

1 Like