Contribute to this guideReport an issue

guideAdvanced setup

The Installation guide describes the easiest ways to run CKEditor builds in your project and the Custom builds guide explains how to add or remove features from the build or change webpack configuration.

In this guide, we would like to show you ways to closer integrate CKEditor with your application. Thanks to that, you will be able to optimize the bundling process of your project and customize the builds in a more convenient way.

# Requirements

In order to start developing CKEditor 5 you will require:

  • Node.js 6.9.0+
  • npm 4+ (note: some npm 5+ versions were known to cause problems, especially with deduplicating packages; upgrade npm when in doubt)
  • Git

# Bundler

CKEditor 5 is currently built using webpack@4. All builds, examples and demos are generated using this bundler. It should also be possible to build CKEditor using other bundlers (if they are configured properly), such as Rollup or Browserify, but these setups are not officially supported yet. Also, the @ckeditor/ckeditor5-dev-webpack-plugin that allows to localize the editor is only available for webpack. More work on this subject will be done in the future.

Therefore, a prerequisite to this guide is that you are using webpack as your build tool.

# Scenario 1: Integrating existing builds

This is the simplest scenario. It assumes that you want to use one of the existing builds “as-is” (you can, of course, still configure the editor). It also gives the fastest build times.

First, install the build of your choice from npm:

npm install --save @ckeditor/ckeditor5-build-classic

Now, import the editor build into your code:

// Using ES6 imports:
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';

// Or CJS imports:
const ClassicEditor = require( '@ckeditor/ckeditor5-build-classic' );

And use it:

ClassicEditor
    .create( document.querySelector( '#editor' ) )
    .then( editor => {
        console.log( editor );
    } )
    .catch( error => {
        console.error( error );
    } );

Since you are using an already built editor (so a result of passing CKEditor 5’s source through webpack), you do not need any additional webpack configuration. In this case CKEditor works as a ready-to-use library.

# Scenario 2: Building from source

This scenario allows you to fully control the building process of CKEditor. This means that you will not actually use the builds anymore, but instead build CKEditor from source directly into your project. This integration method gives you full control over which features will be included and how webpack will be configured.

Similar results to what this method allows can be achieved by customizing an existing build and integrating your custom build like in scenario 1. This will give faster build times (since CKEditor will be built once and committed), however, it requires maintaining a separate repository and installing the code from that repository into your project (e.g. by publishing a new npm package or using tools like Lerna). This makes it less convenient than the method described in this scenario.

First of all, you need to install source packages that you will use. If you base your integration on one of the existing builds, you can take them from that build’s package.json file (see e.g. classic build’s package.json). At this moment you can choose the editor creator and the features you want.

Copy these dependencies to your package.json and call npm install to install them. The dependencies (or devDependencies) section of package.json should look more or less like this:

"dependencies": {
    // ...

    "@ckeditor/ckeditor5-adapter-ckfinder": "^x.y.z",
    "@ckeditor/ckeditor5-autoformat": "^x.y.z",
    "@ckeditor/ckeditor5-basic-styles": "^x.y.z",
    "@ckeditor/ckeditor5-block-quote": "^x.y.z",
    "@ckeditor/ckeditor5-easy-image": "^x.y.z",
    "@ckeditor/ckeditor5-editor-classic": "^x.y.z",
    "@ckeditor/ckeditor5-essentials": "^x.y.z",
    "@ckeditor/ckeditor5-heading": "^x.y.z",
    "@ckeditor/ckeditor5-image": "^x.y.z",
    "@ckeditor/ckeditor5-link": "^x.y.z",
    "@ckeditor/ckeditor5-list": "^x.y.z",
    "@ckeditor/ckeditor5-paragraph": "^x.y.z",
    "@ckeditor/ckeditor5-theme-lark": "^x.y.z",
    "@ckeditor/ckeditor5-upload": "^x.y.z"

    // ...
}

The second step is to install dependencies needed to build the editor. The list may differ if you want to customize the webpack configuration, but this is a typical setup:

npm install --save \
    @ckeditor/ckeditor5-dev-webpack-plugin \
    @ckeditor/ckeditor5-dev-utils \
    postcss-loader \
    raw-loader \
    style-loader \
    webpack@4 \
    webpack-cli@3 \

# Webpack configuration

You can now configure webpack. There are a couple of things that you need to take care of when building CKEditor:

  • Handling CSS files of the CKEditor theme. They are included in the CKEditor sources using import 'path/to/styles.css' statements, so you need proper loaders.
  • Similarly, you need to handle bundling SVG icons, which are also imported directly into the source. For that you need the raw-loader.
  • Finally, to localize the editor you need to use the @ckeditor/ckeditor5-dev-webpack-plugin webpack plugin.

The minimal configuration, assuming that you use the same methods of handling assets as CKEditor builds, will look like this:

const CKEditorWebpackPlugin = require( '@ckeditor/ckeditor5-dev-webpack-plugin' );
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );

module.exports = {
    plugins: [
        // ...

        new CKEditorWebpackPlugin( {
            // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
            language: 'pl'
        } )
    ],

    module: {
        rules: [
            {
                // Or /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg$/ if you want to limit this loader
                // to CKEditor 5 icons only.
                test: /\.svg$/,

                use: [ 'raw-loader' ]
            },
            {
                // Or /ckeditor5-[^/]+\/theme\/[\w-/]+\.css$/ if you want to limit this loader
                // to CKEditor 5 theme only.
                test: /\.css$/,
                use: [
                    {
                        loader: 'style-loader',
                        options: {
                            singleton: true
                        }
                    },
                    {
                        loader: 'postcss-loader',
                        options: styles.getPostCssConfig( {
                            themeImporter: {
                                themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
                            },
                            minify: true
                        } )
                    },
                ]
            }
        ]
    }
};

# Running the editor – method 1

You can now import all the needed plugins and the creator directly into your code and use it there. The easiest way to do so is to copy it from the src/ckeditor.js file available in every build repository.

import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials';
import UploadAdapterPlugin from '@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter';
import AutoformatPlugin from '@ckeditor/ckeditor5-autoformat/src/autoformat';
import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold';
import ItalicPlugin from '@ckeditor/ckeditor5-basic-styles/src/italic';
import BlockQuotePlugin from '@ckeditor/ckeditor5-block-quote/src/blockquote';
import EasyImagePlugin from '@ckeditor/ckeditor5-easy-image/src/easyimage';
import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading';
import ImagePlugin from '@ckeditor/ckeditor5-image/src/image';
import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption';
import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle';
import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar';
import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload';
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
import ListPlugin from '@ckeditor/ckeditor5-list/src/list';
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';

export default class ClassicEditor extends ClassicEditorBase {}

ClassicEditor.builtinPlugins = [
    EssentialsPlugin,
    UploadAdapterPlugin,
    AutoformatPlugin,
    BoldPlugin,
    ItalicPlugin,
    BlockQuotePlugin,
    EasyImagePlugin,
    HeadingPlugin,
    ImagePlugin,
    ImageCaptionPlugin,
    ImageStylePlugin,
    ImageToolbarPlugin,
    ImageUploadPlugin,
    LinkPlugin,
    ListPlugin,
    ParagraphPlugin
];

ClassicEditor.defaultConfig = {
    toolbar: {
        items: [
            'heading',
            '|',
            'bold',
            'italic',
            'link',
            'bulletedList',
            'numberedList',
            'imageUpload',
            'blockQuote',
            'undo',
            'redo'
        ]
    },
    image: {
        toolbar: [
            'imageStyle:full',
            'imageStyle:side',
            '|',
            'imageTextAlternative'
        ]
    },
    language: 'en'
};

This module will export an editor creator class which has all the plugins and configuration that you need already built-in. To use such editor, simply import that class and call the static .create() method like in all examples.

import ClassicEditor from './ckeditor';

ClassicEditor
    // Note that you do not have to specify the plugin and toolbar configuration — using defaults from the build.
    .create( document.querySelector( '#editor' ) )
    .then( editor => {
        console.log( 'Editor was initialized', editor );
    } )
    .catch( error => {
        console.error( error.stack );
    } );

# Running the editor – method 2

The second variant how to run the editor is to use the creator class directly, without creating an intermediary subclass. The above code would translate to:

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials';
import UploadAdapterPlugin from '@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter';
import AutoformatPlugin from '@ckeditor/ckeditor5-autoformat/src/autoformat';
import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold';
import ItalicPlugin from '@ckeditor/ckeditor5-basic-styles/src/italic';
import BlockQuotePlugin from '@ckeditor/ckeditor5-block-quote/src/blockquote';
import EasyImagePlugin from '@ckeditor/ckeditor5-easy-image/src/easyimage';
import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading';
import ImagePlugin from '@ckeditor/ckeditor5-image/src/image';
import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption';
import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle';
import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar';
import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload';
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
import ListPlugin from '@ckeditor/ckeditor5-list/src/list';
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';

ClassicEditor
    .create( document.querySelector( '#editor'), {
        // The plugins are now passed directly to .create().
        plugins: [
            EssentialsPlugin,
            AutoformatPlugin,
            BoldPlugin,
            ItalicPlugin,
            BlockQuotePlugin,
            HeadingPlugin,
            ImagePlugin,
            ImageCaptionPlugin,
            ImageStylePlugin,
            ImageToolbarPlugin,
            LinkPlugin,
            ListPlugin,
            ParagraphPlugin
        ],

        // So is the rest of the default configuration.
        toolbar: [
            'heading',
            'bold',
            'italic',
            'link',
            'bulletedList',
            'numberedList',
            'imageUpload',
            'blockQuote',
            'undo',
            'redo'
        ],
        image: {
            toolbar: [
                'imageStyle:full',
                'imageStyle:side',
                '|',
                'imageTextAlternative'
            ]
        }
    } )
    .then( editor => {
        console.log( editor );
    } )
    .catch( error => {
        console.error( error );
    } );

# Building

Finally, you can build your application. Run webpack on your project and the editor will be a part of it.

# Option: Minifying JavaScript

Webpack 4 introduced the concept of modes. It comes with two predefined modes — development and production. The latter automatically enables uglifyjs-webpack-plugin which takes care of JavaScript minification. Therefore, it is enough to execute webpack with the --mode production option or set mode: 'production' in your webpack.config.js to optimize the build.

Prior to version 1.2.7 uglifyjs-webpack-plugin had a bug which caused webpack to crash with the following error: TypeError: Assignment to constant variable.. If you experienced this error, make sure that your node_modules contains an up-to-date version of this package (and that webpack uses this version).

# Option: Extracting CSS

One of the most common requirements is to extract CKEditor’s CSS to a separate file (by default it is included in the output JavaScript file). To do that, you can use mini-css-extract-plugin:

npm install --save \
    mini-css-extract-plugin \
    css-loader

And add it to your webpack configuration:

const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );

module.exports = {
    // ...

    plugins: [
        // ...

        new MiniCssExtractPlugin( {
            filename: 'styles.css'
        } )
    ],

    module: {
        rules: [
            {
                test: /\.svg$/,
                use: [ 'raw-loader' ]
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: styles.getPostCssConfig( {
                            themeImporter: {
                                themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
                            },
                            minify: true
                        } )
                    }
                ]
            }
        ]
    }
};

Webpack will now create a separate file called styles.css which you will need to load manually into your HTML (using the <link rel="stylesheet"> tag).

# Option: Building to ES5 target

CKEditor 5 is written in ECMAScript 2015 (also called ES6). All browsers in which CKEditor 5 is currently supported have sufficient ES6 support to run CKEditor 5. Thanks to that, CKEditor 5 Builds are also published in the original ES6 format.

However, it may happen that your environment requires ES5. For instance, if you use tools like the original UglifyJS which do not support ES6+ yet, you may need to transpile CKEditor 5 source to ES5. This will create ~80% bigger builds but will ensure that your environment can process CKEditor 5 code.

In the production mode webpack uses uglifyjs-webpack-plugin which supports ES6+ code. This is because it does not use the original UglifyJS plugin (which does not support ES6+), but instead it uses the uglify-es package.

We recommend upgrading your setup to webpack@4 and its built-in modes which allows you to avoid transpiling the source to ES5.

In order to create an ES5 build of CKEditor 5 you can use Babel:

npm install --save babel-loader babel-core babel-preset-env regenerator-runtime

Then, add this item to webpack module.rules section:

module: {
    rules: [
        {
            test: /\.js$/,
            use: [
                {
                    loader: 'babel-loader',
                    options: {
                        presets: [ require( 'babel-preset-env' ) ]
                    }
                }
            ]
        },
        ...
    ]
}

And load regenerator-runtime (needed to make ES6 generators work after transpilation) by adding it as the first entry point:

entry: [
    require.resolve( 'regenerator-runtime/runtime.js' ),

    // Your entries...
]

This setup ensures that the source code is transpiled to ES5. However, it does not ensure that all ES6 polyfills are loaded. Therefore, if you would like to, for example, give bringing IE11 compatibility a try, make sure to also load babel-polyfill.

The babel-preset-env package lets you choose the environment that you want to support and transpiles ES6+ features to match that environment’s capabilities. Without configuration it will produce ES5 builds.