Sign up (with export icon)

Integrating CKEditor 5 with Vue.js 3+ from CDN

Contribute to this guideShow the table of contents

CKEditor 5 has an official Vue integration that you can use to add a rich text editor to your application. This guide will help you install it and configure to use the CDN distribution of the CKEditor 5.

Create your own CKEditor 5

Check out our interactive Builder to quickly get a taste of CKEditor 5. It offers an easy-to-use user interface to help you configure, preview, and download the editor suited to your needs.

  • editor type,
  • the features you need,
  • the preferred framework (React, Angular, Vue or Vanilla JS),
  • the preferred distribution method.

You get ready-to-use code tailored to your needs!

Check out our interactive Builder

Quick start

Copy link

This guide assumes that you already have a Vue project. If you do not have one, see the Vue documentation to learn how to create it.

Note

To use our Cloud CDN services, create a free account. Learn more about license key activation.

Start by installing the Vue integration for CKEditor 5 from npm:

npm install @ckeditor/ckeditor5-vue
Copy code

Once the integration is installed, create a new Vue component called Editor.vue. It will use the useCKEditorCloud helper to load the editor code from the CDN and the <ckeditor> component to run it, both of which come from the above package. The following example shows a single file component with open source and premium CKEditor 5 plugins.

<template>
    <ckeditor
        v-if="editor"
        v-model="data"
        :editor="editor"
        :config="config"
    />
</template>

<script setup>
import { ref, computed } from 'vue';
import { Ckeditor, useCKEditorCloud } from '@ckeditor/ckeditor5-vue';

const cloud = useCKEditorCloud( {
    version: '48.1.0',
    premium: true
} );

const data = ref( '<p>Hello world!</p>' );

const editor = computed( () => {
    if ( !cloud.data.value ) {
        return null;
    }

    return cloud.data.value.CKEditor.ClassicEditor;
} );

const config = computed( () => {
        if ( !cloud.data.value ) {
        return null;
    }

    const { Essentials, Paragraph, Bold, Italic } = cloud.data.value.CKEditor;
    const { FormatPainter } = cloud.data.value.CKEditorPremiumFeatures;

    return {
        licenseKey: '<YOUR_LICENSE_KEY>',
        plugins: [ Essentials, Paragraph, Bold, Italic, FormatPainter ],
        toolbar: [ 'undo', 'redo', '|', 'bold', 'italic', '|', 'formatPainter' ]
    };
} );
</script>
Copy code

In the above example, the useCKEditorCloud helper is used to load the editor code and plugins from CDN. The premium option is set to also load premium plugins. For more information about the useCKEditorCloud helper, see the Loading CDN resources guide.

Now, you can import and use the Editor.vue component anywhere in your application.

<template>
    <Editor />
</template>
Copy code

If you use Nuxt.js with server-side rendering enabled, remember to wrap the <Editor> component in the <ClientOnly> component to avoid issues with the editor calling browser-specific APIs on the server.

<template>
    <ClientOnly>
        <Editor />
    </ClientOnly>
</template>
Copy code

Component directives

Copy link

editor

Copy link

This directive specifies the editor to be used by the component. It must directly reference the editor constructor to be used in the template.

<template>
    <ckeditor :editor="editor" />
</template>
Copy code

tag-name

Copy link

By default, the editor component creates a <div> container which is used as an element passed to the editor (for example, ClassicEditor#element). The element can be configured, so for example to create a <textarea>, use the following directive:

<ckeditor :editor="editor" tag-name="textarea" />
Copy code

v-model

Copy link

A standard directive for form inputs in Vue. Unlike model-value, it creates a two–way data binding, which:

  • Sets the initial editor content.
  • Automatically updates the state of the application as the editor content changes (for example, as the user types).
  • Can be used to set the editor content when necessary.
<template>
    <ckeditor :editor="editor" v-model="data" />
    <button @click="emptyEditor">Empty the editor</button>

    <h2>Editor data</h2>
    <code>{{ data }}</code>
</template>

<script setup>
import { ref } from 'vue';
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.

const data = ref( '<p>Hello world!</p>' );

function emptyEditor() {
    data.value = '';
}
</script>
Copy code

In the above example, the data property will be updated automatically as the user types and the content changes. It can also be used to change (as in emptyEditor()) or set the initial content of the editor.

If you only want to execute an action when the editor data changes, use the input event.

model-value

Copy link

Allows a one–way data binding that sets the content of the editor. Unlike v-model, the value will not be updated when the content of the editor changes.

<template>
    <ckeditor :editor="editor" :model-value="data" />
</template>

<script setup>
import { ref } from 'vue';
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.

const data = ref( '<p>Hello world!</p>' );
</script>
Copy code

To execute an action when the editor data changes, use the input event.

config

Copy link

Specifies the configuration of the editor.

<template>
    <ckeditor :editor="editor" :config="config" />
</template>

<script setup>
import { computed } from 'vue';
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.

const config = computed( () => {
    const { Essentials, Paragraph, Bold, Italic } = cloud.data.value.CKEditor;
    const { FormatPainter } = cloud.data.value.CKEditorPremiumFeatures;

    return {
        licenseKey: '<YOUR_LICENSE_KEY>',
        plugins: [ Essentials, Paragraph, Bold, Italic, FormatPainter ],
        toolbar: [ 'undo', 'redo', '|', 'bold', 'italic', '|', 'formatPainter' ]
    };
} );
</script>
Copy code

disabled

Copy link

This directive controls the isReadOnly property of the editor.

It sets the initial read–only state of the editor and changes it during its lifecycle.

<template>
    <ckeditor :editor="editor" :disabled="disabled" />
</template>

<script setup>
import { ref } from 'vue';
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.

const disabled = ref( true );
</script>
Copy code

disable-two-way-data-binding

Copy link

Allows disabling the two-way data binding mechanism. The default value is false.

The reason for introducing this option is performance issues in large documents. After enabling this flag, the v-model directive will no longer update the connected value whenever the editor’s data is changed.

This option allows the integrator to disable the default behavior and only call the editor.getData() method on demand, which prevents the slowdowns. You can read more in the relevant issue.

<template>
    <ckeditor
        :editor="editor"
        :disable-two-way-data-binding="disableTwoWayDataBinding"
    />
</template>

<script setup>
import { ref } from 'vue';
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.

const disableTwoWayDataBinding = ref( true );
</script>
Copy code

watchdog-config

Copy link

Allows passing a configuration object to the underlying EditorWatchdog. By default, the <ckeditor> component automatically wraps the editor with a watchdog that detects crashes and restarts the editor to recover lost content. Use this prop to customize the watchdog behavior, such as the number of allowed crashes before the watchdog gives up, or the minimum time between crashes.

<template>
    <ckeditor
        :editor="editor"
        :watchdog-config="watchdogConfig"
    />
</template>

<script setup>
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.

const watchdogConfig = {
    crashNumberLimit: 5,
    minimumNonErrorTimePeriod: 2000
};
</script>
Copy code

See the WatchdogConfig API for the full list of available options.

This prop has no effect when disable-watchdog is set to true.

disable-watchdog

Copy link

Allows disabling the built-in watchdog. The default value is false.

By default, the <ckeditor> component wraps the editor with CKEditor 5’s EditorWatchdog, which automatically detects and recovers from editor crashes. Setting disable-watchdog to true opts out of this behavior — the editor will run without crash recovery.

When the watchdog is disabled, the ready and destroy events will each fire at most once during the component’s lifetime, and the error event will never be emitted.

<template>
    <ckeditor :editor="editor" :disable-watchdog="true" />
</template>

<script setup>
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.
</script>
Copy code

Component events

Copy link

ready

Copy link

Corresponds to the ready editor event.

<ckeditor :editor="editor" @ready="onEditorReady" />
Copy code
Note

When the watchdog is active (the default), this event can fire multiple times during the component’s lifetime — once after the initial mount and again after each watchdog-triggered editor restart. If you need one-time initialization logic (for example, inserting a toolbar into the DOM for the Document editor type), make sure your handler is idempotent or guard it with a flag.

focus

Copy link

Corresponds to the focus editor event.

<ckeditor :editor="editor" @focus="onEditorFocus" />
Copy code

blur

Copy link

Corresponds to the blur editor event.

<ckeditor :editor="editor" @blur="onEditorBlur" />
Copy code

input

Copy link

Corresponds to the change:data editor event.

<ckeditor :editor="editor" @input="onEditorInput" />
Copy code

error

Copy link

Fired when an error is detected by the watchdog — either during editor initialization or at runtime.

<ckeditor :editor="editor" @error="onEditorError" />
Copy code

The event handler receives two arguments:

  • error – the Error object describing what went wrong.
  • details – an object with the following properties:
    • phase: 'initialization' | 'runtime''initialization' when the error occurred during Editor.create(), or 'runtime' for errors caught during normal operation.
    • causesRestart: boolean – whether the watchdog will attempt to restart the editor. When false, no automatic restart is scheduled (for example, the crash limit was reached, or restarting does not apply to this error).
<template>
    <ckeditor :editor="editor" @error="onEditorError" />
</template>

<script setup>
import { Ckeditor } from '@ckeditor/ckeditor5-vue';

// Editor loading and configuration is skipped for brevity.

function onEditorError( error, { phase, causesRestart } ) {
    if ( phase === 'runtime' && causesRestart ) {
        console.warn( 'Editor crashed: the watchdog is restarting it.', error );
    } else {
        console.error( 'Editor error: the watchdog will not restart the editor automatically.', error );
    }
}
</script>
Copy code

This event is not emitted when disable-watchdog is set to true.

destroy

Copy link

Corresponds to the destroy editor event.

<ckeditor :editor="editor" @destroy="onEditorDestroy" />
Copy code
Note

Because the destruction of the editor is promise–driven, this event can be fired before the actual promise resolves.

Note

When the watchdog is active (the default), this event can fire multiple times during the component’s lifetime — once for each editor instance destroyed during a watchdog restart. It is not fired when the component unmounts before the editor finishes initializing. If you need to react to component unmount, use Vue’s onBeforeUnmount lifecycle hook instead.

How to?

Copy link

Using the Document editor type

Copy link

If you use the Document (decoupled) editor in your application, you need to manually add the editor toolbar to the DOM.

Since accessing the editor toolbar is not possible until after the editor instance is ready, put your toolbar insertion code in a method executed upon the ready event of the component, like in the following example:

<template>
    <ckeditor
        v-if="editor"
        v-model="data"
        :editor="editor"
        :config="config"
        @ready="onReady"
    />
</template>

<script setup>
import { ref, computed } from 'vue';
import { Ckeditor, useCKEditorCloud } from '@ckeditor/ckeditor5-vue';

const cloud = useCKEditorCloud( {
    version: '48.1.0'
} );

const data = ref( '<p>Hello world!</p>' );

const editor = computed( () => {
    if ( !cloud.data.value ) {
        return null;
    }

    return cloud.data.value.CKEditor.ClassicEditor;
} );

const config = computed( () => {
    if ( !cloud.data.value ) {
        return null;
    }

    const { Essentials, Paragraph, Bold, Italic, Mention } = cloud.data.value.CKEditor;
    const { SlashCommand } = cloud.data.value.CKEditorPremiumFeatures;

    return {
        licenseKey: '<YOUR_LICENSE_KEY>',
        toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ],
        plugins: [
            Essentials,
            Paragraph,
            Bold,
            Italic,
            Mention,
            SlashCommand
        ]
    };
} );

function onReady( editor ) {
    // Insert the toolbar before the editable area.
    editor.ui.getEditableElement().parentElement.insertBefore(
        editor.ui.view.toolbar.element,
        editor.ui.getEditableElement()
    );
}
</script>
Copy code

Using the editor with collaboration plugins

Copy link

We provide a ready-to-use integration featuring collaborative editing in a Vue application:

It is not mandatory to build applications on top of the above sample, however, it should help you get started.

Localization

Copy link

CKEditor 5 supports multiple UI languages, and so does the official Vue component. To translate the editor, pass the languages you need into the translations array inside the configuration of the useCKEditorCloud function.

<script setup>
import { useCKEditorCloud } from '@ckeditor/ckeditor5-vue';

const cloud = useCKEditorCloud( {
    version: '48.1.0',
    translations: [ 'es' ]
} );
</script>
Copy code

TypeScript support

Copy link

The CKEditor 5 Vue component is written in TypeScript and provides type definitions. If you use TypeScript in your project, you can take advantage of them. To do so, import the component and its types using an import type statement from a special package containing type definitions. Take a look at the following example:

<script setup>
import { useCKEditorCloud } from '@ckeditor/ckeditor5-vue';
import type { ClassicEditor } from 'https://cdn.ckeditor.com/typings/ckeditor5.d.ts';

const cloud = useCKEditorCloud( {
    version: '48.1.0',
    translations: [ 'es' ]
} );

const TestEditor = computed<typeof ClassicEditor | null>( () => {
    if ( !cloud.data.value ) {
        return null;
    }

    const {
        ClassicEditor: BaseEditor,
        Paragraph,
        Essentials,
        Heading,
        Bold,
        Italic
    } = cloud.data.value.CKEditor;

    return class TestEditor extends BaseEditor {
        static builtinPlugins = [
            Essentials,
            Paragraph,
            Heading,
            Bold,
            Italic
        ];
    };
} );
</script>
Copy code

In the example above, the ClassicEditor type is imported from the https://cdn.ckeditor.com/typings/ckeditor5.d.ts package, while the editor itself loads from the CDN. Note that https://cdn.ckeditor.com/typings/ckeditor5.d.ts is not an actual URL to the CKEditor 5 types file but a synthetic TypeScript module providing type definitions for the editor. The ckeditor5 package supplies the actual typings, which depend on the @ckeditor/ckeditor5-react package.

Although this setup might seem complex, it prevents users from directly importing anything from the ckeditor5 package, which could lead to duplicated code issues.

Type definitions for premium features

Copy link

If you want to use types for premium features, you can import them similarly to the base editor types. Remember that you need to install the ckeditor5-premium-features package to use them. You can do it by running the following command:

npm install --save-dev ckeditor5-premium-features
Copy code

After installing the package, you can import the types in the following way:

<script setup>
// ...
import type { Mention } from 'https://cdn.ckeditor.com/typings/ckeditor5-premium-features.d.ts';
// ...
</script>
Copy code

Known issues

Copy link

While type definitions for the base editor should be available out of the box, some bundlers do not install the ckeditor5 package, which provides typing for the editor. If you encounter any issues with the type definitions, you can install the ckeditor5 package manually:

npm install --save-dev ckeditor5
Copy code

Contributing and reporting issues

Copy link

The source code of this component is available on GitHub in https://github.com/ckeditor/ckeditor5-vue.

Next steps

Copy link