CKEditor 5
Replaces the default Strapi WYSIWYG editor with a customized build of CKEditor 5 packed with useful plugins. (Unofficial integration)
Strapi ➕ CKEditor
Integrates CKEditor 5 into your Strapi project as a fully customizable custom field. (Community edition)
👋 Get Started
✨ Features
- Extensive configuration
- Media Library integration
- Dark theme support
- Fullscreen expansion
- Responsive images support
- Automatic UI translations
- i18n support
- Self-Hosted
🔧 Installation
Strapi v5:
Add the package to your Strapi application:
yarn add @_sh/strapi-plugin-ckeditor
Then, build the app:
yarn build
Strapi v4:
Version 3 of the plugin will remain available for Strapi v4 until March 2026. Refer to the v3 installation guide for setup instructions.
Note: Version 3 receives only essential bug fixes. The configuration process and available features in version 3 differ significantly from the latest plugin versions.
✍️ Usage
The field can be found in the Content-Type Builder under the CUSTOM
tab:
Presets
A preset is a set of settings that define the editor's features and appearance. You can specify a dedicated preset for each field. The available presets can be customized through the configuration.
The plugin provides two presets out of the box: Default HTML editor
and Default Markdown editor
,
each has different output format: HTML and Markdown, respectively. Both presets include
an extensive set of non-premium CKEditor plugins.
Default HTML editor:
Default Markdown editor:
Responsive images
The plugin generates srcset
attribute for inserted images if the image has any formats
other than thumbnail
.
UI language: If you don't specify the UI language in the editor configuration, the plugin will default to the Strapi admin's preferred language. If no preference is set, English will be used as a fallback.
Content language: i18n for the editor's content language can be enabled by checking the
Enable localization for this field
option in the Advanced Settings tab.
💡 It is important to use the content styles on the publishing side of your application. Otherwise, the content will look different in the editor and for your end users. Follow the documentation.
⚙️ Configuration
The plugin configuration must be defined on the front-end. The plugin provides a setPluginConfig
function, which accepts a plugin configuration (PluginConfig
) that can include an array of presets
and a theme object:
1import { setPluginConfig } from '@_sh/strapi-plugin-ckeditor';
2
3function setPluginConfig(pluginConfig: {
4 presets: Preset[] | undefined;
5 theme: Theme | undefined;
6}): void;
Key points:
- The function must be invoked before the admin panel's bootstrap lifecycle function. The general recommendation is to call it inside the admin panel's register lifecycle function.
- Provided properties will overwrite the default configuration values.
- The configuration becomes immutable after the first invocation, preventing further modifications.
Plugin configuration object includes optional presets
and theme
properties:
1type PluginConfig = {
2 /**
3 * Presets are sets of settings that define the editor's features and appearance.
4 */
5 presets?: Preset[];
6 /**
7 * Styles applied globally to every editor instance.
8 */
9 theme?: Theme;
10};
presets
is an array of objects of type Preset
. Each preset includes the following properties:
1type Preset = {
2 /**
3 * Preset name, displayed in the schema.
4 */
5 name: string;
6 /**
7 * Preset description, displayed in the Content-Type Builder.
8 */
9 description: string;
10 /**
11 * CKEditor configuration object.
12 *
13 * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation}
14 */
15 editorConfig: EditorConfig;
16 /**
17 * Additional styles applied to the editor instance after the theme styles.
18 */
19 styles?: EditorStyles;
20};
theme
object defines a plugin CSS styles applied to every editor instance. It includes the
following properties:
1/**
2 * The `common` styles are applied first, followed by `light` or `dark` styles
3 * according to user preferences, and finally `additional` styles.
4 */
5type Theme = {
6 /**
7 * Core styles.
8 */
9 common?: EditorStyles;
10 /**
11 * Styles applied in light mode.
12 */
13 light?: EditorStyles;
14 /**
15 * Styles applied in dark mode.
16 */
17 dark?: EditorStyles;
18 /**
19 * Additional styles that complement the theme.
20 */
21 additional?: EditorStyles;
22};
1/**
2 * Represents styles that can be applied to an editor instance.
3 * Can be a plain CSS string or an array of interpolations for dynamic styling.
4 */
5export type EditorStyles = string | Interpolation<object>[];
Default presets and theme
To simplify the process of defining a new preset, the plugin exports default presets and a default theme, which can be used as a base in custom configurations:
1import {
2 defaultTheme,
3 defaultHtmlPreset,
4 defaultMarkdownPreset,
5} from '@_sh/strapi-plugin-ckeditor';
Integration with Strapi's Media Library
To integrate CKEditor with the Strapi's Media Library, the plugin provides two CKEditor plugins that can be included in your presets without additional configuration:
1import { StrapiMediaLib, StrapiUploadAdapter } from '@_sh/strapi-plugin-ckeditor';
Available type definitions
The following type definitions are available for use:
1import type {
2 PluginConfig,
3 Preset,
4 EditorConfig,
5 Theme,
6 EditorStyles,
7} from '@_sh/strapi-plugin-ckeditor';
Type definitions
1/**
2 * Plugin configuration object.
3 */
4export type PluginConfig = {
5 /**
6 * Presets are sets of settings that define the editor's features and appearance.
7 */
8 presets?: Preset[];
9 /**
10 * Styles applied globally to every editor instance.
11 */
12 theme?: Theme;
13};
1/**
2 * Preset is a set of settings that define the editor's features and appearance.
3 */
4export type Preset = {
5 /**
6 * Preset name, displayed in the schema.
7 */
8 name: string;
9 /**
10 * Preset description, displayed in the Content-Type Builder.
11 */
12 description: string;
13 /**
14 * Additional styles applied to the editor instance after the theme styles.
15 */
16 styles?: EditorStyles;
17 /**
18 * CKEditor configuration object.
19 *
20 * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation}
21 */
22 editorConfig: EditorConfig;
23};
1/**
2 * CKEditor configuration object.
3 *
4 * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation}
5 */
6export type EditorConfig = CKE5EditorConfig;
1/**
2 * Styles applied globally to every editor instance.
3 *
4 * @remarks
5 *
6 * The `common` styles are applied first, followed by `light` or `dark` styles
7 * according to the preferences, and finally `additional` styles.
8 */
9export type Theme = {
10 /**
11 * Core styles.
12 */
13 common?: EditorStyles;
14 /**
15 * Styles applied in light mode.
16 */
17 light?: EditorStyles;
18 /**
19 * Styles applied in dark mode.
20 */
21 dark?: EditorStyles;
22 /**
23 * Additional styles that complement the theme.
24 */
25 additional?: EditorStyles;
26};
1/**
2 * Represents styles that can be applied to an editor instance.
3 * Can be a plain CSS string or an array of interpolations for dynamic styling.
4 */
5export type EditorStyles = string | Interpolation<object>[];
Configuration examples:
Adding a new preset [JS]
1// src/admin/app.js
2
3import {
4 Bold,
5 Italic,
6 Essentials,
7 Heading,
8 Image,
9 ImageCaption,
10 ImageStyle,
11 ImageToolbar,
12 ImageUpload,
13 Link,
14 List,
15 Paragraph,
16} from 'ckeditor5';
17
18import { setPluginConfig, StrapiMediaLib, StrapiUploadAdapter } from '@_sh/strapi-plugin-ckeditor';
19
20const myCustomPreset = {
21 name: 'myCustomPreset',
22 description: 'myCustomPreset description',
23 editorConfig: {
24 licenseKey: 'GPL',
25 plugins: [
26 Bold,
27 Italic,
28 Essentials,
29 Heading,
30 Image,
31 ImageCaption,
32 ImageStyle,
33 ImageToolbar,
34 ImageUpload,
35 Link,
36 List,
37 Paragraph,
38 StrapiMediaLib,
39 StrapiUploadAdapter,
40 ],
41 toolbar: [
42 'heading',
43 '|',
44 'bold',
45 'italic',
46 'link',
47 'bulletedList',
48 'numberedList',
49 '|',
50 'strapiMediaLib',
51 '|',
52 'undo',
53 'redo',
54 ],
55 },
56};
57
58const myConfig = {
59 /**
60 * Note: Since the provided `presets` include only `myCustomPreset`
61 * this configuration will overwrite default presets.
62 */
63 presets: [myCustomPreset],
64};
65
66export default {
67 register() {
68 setPluginConfig(myConfig);
69 },
70};
Adding a new preset [TS]
1// src/admin/app.tsx
2
3import {
4 Bold,
5 Italic,
6 Essentials,
7 Heading,
8 Image,
9 ImageCaption,
10 ImageStyle,
11 ImageToolbar,
12 ImageUpload,
13 Link,
14 List,
15 Paragraph,
16} from 'ckeditor5';
17
18import {
19 type PluginConfig,
20 type Preset,
21 setPluginConfig,
22 StrapiMediaLib,
23 StrapiUploadAdapter,
24} from '@_sh/strapi-plugin-ckeditor';
25
26const myCustomPreset: Preset = {
27 name: 'myCustomPreset',
28 description: 'myCustomPreset description',
29 editorConfig: {
30 licenseKey: 'GPL',
31 plugins: [
32 Bold,
33 Italic,
34 Essentials,
35 Heading,
36 Image,
37 ImageCaption,
38 ImageStyle,
39 ImageToolbar,
40 ImageUpload,
41 Link,
42 List,
43 Paragraph,
44 StrapiMediaLib,
45 StrapiUploadAdapter,
46 ],
47 toolbar: [
48 'heading',
49 '|',
50 'bold',
51 'italic',
52 'link',
53 'bulletedList',
54 'numberedList',
55 '|',
56 'strapiMediaLib',
57 '|',
58 'undo',
59 'redo',
60 ],
61 },
62};
63
64const myConfig: PluginConfig = {
65 /**
66 * Note that since provided `presets` includes only `myCustomPreset`
67 * this configuration will overwrite default presets.
68 */
69 presets: [myCustomPreset],
70};
71
72export default {
73 register() {
74 setPluginConfig(myConfig);
75 },
76};
Default presets modification [TS]
1// src/admin/app.tsx
2
3import { css } from 'styled-components';
4
5import {
6 type PluginConfig,
7 type Preset,
8 setPluginConfig,
9 defaultHtmlPreset,
10 defaultMarkdownPreset,
11} from '@_sh/strapi-plugin-ckeditor';
12
13const defaultHtml: Preset = {
14 ...defaultHtmlPreset,
15 description: 'Modified default HTML editor',
16 styles: `
17 .ck {
18 color: red;
19 }
20 `,
21 editorConfig: {
22 ...defaultHtmlPreset.editorConfig,
23 placeholder: 'Modified default HTML editor',
24 toolbar: [
25 'heading',
26 '|',
27 'bold',
28 'italic',
29 'link',
30 'bulletedList',
31 'numberedList',
32 '|',
33 'strapiMediaLib',
34 'insertTable',
35 '|',
36 'undo',
37 'redo',
38 ],
39 },
40};
41
42const defaultMarkdown: Preset = {
43 ...defaultMarkdownPreset,
44 description: 'Modified default Markdown editor',
45 styles: css`
46 .ck {
47 --ck-editor-max-width: 1500px;
48 --ck-editor-min-height: 700px;
49 --ck-editor-max-height: 700px;
50 }
51
52 .ck.ck-editor__main {
53 border: 3px dashed ${({ theme }) => theme.colors.warning500};
54 }
55 `,
56};
57
58const myConfig: PluginConfig = {
59 presets: [defaultHtml, defaultMarkdown],
60};
61
62export default {
63 register() {
64 setPluginConfig(myConfig);
65 },
66};
Modifying theme [TS]
1// src/admin/app.tsx
2
3import { css } from 'styled-components';
4
5import { type PluginConfig, setPluginConfig, defaultTheme } from '@_sh/strapi-plugin-ckeditor';
6
7const myConfig: PluginConfig = {
8 theme: {
9 ...defaultTheme,
10 additional: css`
11 .ck {
12 --ck-editor-max-width: 1500px;
13 --ck-editor-min-height: 700px;
14 --ck-editor-max-height: 700px;
15 }
16
17 .ck.ck-editor__main {
18 border: 3px dashed ${({ theme }) => theme.colors.warning500};
19 }
20 `,
21 },
22};
23
24export default {
25 register() {
26 setPluginConfig(myConfig);
27 },
28};
Adding Timestamp plugin [JS]
1// src/admin/app.js
2
3import { Plugin, ButtonView } from 'ckeditor5';
4
5import { setPluginConfig, defaultHtmlPreset } from '@_sh/strapi-plugin-ckeditor';
6
7class Timestamp extends Plugin {
8 init() {
9 const editor = this.editor;
10 editor.ui.componentFactory.add('timestamp', () => {
11 const button = new ButtonView();
12 button.set({
13 label: 'timestamp',
14 withText: true,
15 });
16 button.on('execute', () => {
17 const now = new Date();
18 editor.model.change(writer => {
19 editor.model.insertContent(writer.createText(now.toString()));
20 });
21 });
22 return button;
23 });
24 }
25}
26
27export default {
28 register() {
29 defaultHtmlPreset.editorConfig.plugins.push(Timestamp);
30 defaultHtmlPreset.editorConfig.toolbar.unshift('timestamp');
31 setPluginConfig({ presets: [defaultHtmlPreset] });
32 },
33};
📂 Default HTML preset: admin/src/config/htmlPreset.ts
📂 Default Markdown preset: admin/src/config/markdownPreset.ts
📂 Default theme: admin/src/theme
📌 It is highly recommended to explore the official CKEditor5 documentation.
💡 To display content from external sources, such as images or videos, in your admin panel, you need to configure your
middlewares.js
file. Check the documentation for details.
🛠 Contributing
Feel free to fork the repository and open a pull request, any help is appreciated.
Follow these steps to set up a plugin development environment:
Clone the repository.
Navigate to the cloned plugin folder and install dependencies, run:
yarn install
Link the plugin to your project:
- In the plugin folder, run:
yarn watch:link
- Open a new terminal, navigate to your Strapi project directory, and run:
yarn dlx yalc add --link @_sh/strapi-plugin-ckeditor && yarn install
Rebuild the project and start the server:
yarn build
yarn develop
✈️ Migration
To upgrade to the latest version, follow the dedicated migration guide for detailed instructions.
⚠️ Requirements
v5.x.x
Strapi >= 5.0.0
Node >= 18.0.0 <= 22.x.x
v3.x.x
Strapi >= 4.13.0 < 5.0.0
Node >= 18.0.0 <= 20.x.x
Install now
npm install @_sh/strapi-plugin-ckeditor
Create your own plugin
Check out the available plugin resources that will help you to develop your plugin or provider and get it listed on the marketplace.