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.
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.
⚙️ Configuration
The plugin configuration must be defined on the front-end.
The plugin provides a set of functions that allow you to modify the plugin's configuration:
1setPluginConfig(config);
2getPluginPresets();
3getPluginTheme();
setPluginConfig
The function, 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>[];
getPluginPresets
1function getPluginPresets(): {
2 [key: string]: Preset;
3};
Returns presets
object.
- Each property name must match the corresponding preset's name.
- To extend or modify the options visible in the admin panel's content manager, changes must be made before the admin panel's bootstrap lifecycle function.
getPluginTheme
1function getPluginTheme(): Theme;
Returns theme
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:
Setting a new set of presets [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};
Setting a new set of presets [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 using setPluginConfig [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};
Default presets modification using getPluginPresets [TS]
1// src/admin/app.tsx
2
3import { css } from 'styled-components';
4import { getPluginPresets } from '@_sh/strapi-plugin-ckeditor';
5
6export default {
7 register() {
8 const presets = getPluginPresets();
9
10 presets.defaultHtml.styles = css`
11 .ck {
12 color: red;
13 }
14 `;
15
16 presets.defaultHtml.editorConfig = {
17 ...presets.defaultHtml.editorConfig,
18 placeholder: 'Modified default HTML editor',
19 toolbar: [
20 'heading',
21 '|',
22 'bold',
23 'italic',
24 'link',
25 'bulletedList',
26 'numberedList',
27 '|',
28 'strapiMediaLib',
29 'insertTable',
30 '|',
31 'undo',
32 'redo',
33 ],
34 };
35
36 presets.defaultMarkdown = {
37 ...presets.defaultMarkdown,
38 description: 'Modified default Markdown editor',
39 styles: css`
40 .ck {
41 --ck-editor-max-width: 1500px;
42 --ck-editor-min-height: 700px;
43 --ck-editor-max-height: 700px;
44 }
45
46 .ck.ck-editor__main {
47 border: 3px dashed ${({ theme }) => theme.colors.warning500};
48 }
49 `,
50 };
51 },
52};
Modifying theme using setPluginConfig [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};
Modifying theme using getPluginTheme [TS]
1// src/admin/app.tsx
2
3import { css } from 'styled-components';
4import { getPluginTheme } from '@_sh/strapi-plugin-ckeditor';
5
6export default {
7 register() {
8 const theme = getPluginTheme();
9
10 theme.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};
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.
🛠 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.