motorscript.com

Using CKEditor 5 with Nuxt.js

Published:

CKEditor 5 is built for browsers and although ES6 imports are available it cannot be used directly in Nuxt.js with Server Side Rendering (SSR) because the sources reference to window object.

Importing CKEditor in components with Nuxt running in universal mode would give the following error:

ERROR  window is not defined
  at Object.<anonymous> (node_modules/@ckeditor/ckeditor5-build-decoupled-document/build/ckeditor.js:5:2287)

In this example, we will be using Document editor build of CKEditor - DecoupledEditor. Other builds should also work in the same manner.

yarn add @ckeditor/ckeditor5-build-decoupled-document
View CKEditor documentation for more details.

Method 1: Using Plugins

Create plugins/ckeditor.js with the following content:

import DecoupledEditor from '@ckeditor/ckeditor5-build-decoupled-document'
export default DecoupledEditor
In nuxt.config.js, add the newly created plugin only to be used for Client Side Rendering (CSR):
plugins: [
  ...,
  { src: '~plugins/ckeditor', ssr: false }
],
Now DecoupledEditor can be used within Nuxt pages.

WARNING: This method causes Nuxt to include CKEditor in vendor build which will be loaded to render pages even when CKEditor is not required. Therefore, I prefer Method 2.

Method 2: Importing in pages only for Client Side

Nuxt facilitates process.browser which we can use to test if the script is being run on the browser. However, this cannot be used with ES6 imports as we cannot wrap import within an if condition. We will have to resort to using require. This way we will import ckeditor only in the required pages, thus keeping our overall application light. A minimal page using this technique follows.

<template>
  <div class="container">
    <h1>CKEditor 5 Nuxt Demo</h1>
    <div id="toolbar-container"></div>
    <div id="editor"></div>
  </div>
</template>

<script>
let DecoupledEditor
if (process.browser) {
  DecoupledEditor = require('@ckeditor/ckeditor5-build-decoupled-document')
}

export default {
  mounted() {
    DecoupledEditor.create(document.querySelector('#editor'))
      .then((editor) => {
        const toolbarContainer = document.querySelector('#toolbar-container')
        toolbarContainer.appendChild(editor.ui.view.toolbar.element)
      })
      .catch((error) => {
        // eslint-disable-next-line no-console
        console.error(error)
      })
  }
}
</script>

Not So Minimal Demo: Preeti to Unicode converter

Happy Nuxting!