# Advanced editor bundling in Node.js

This article presents how to create a CKEditor 5 [editor bundle](../../guides/collaboration/editor-bundle.md) in Node.js. It is a step-by-step guide on creating an editor bundle from the CKEditor 5 package downloaded from [CKEditor 5 Builder](https://builder.ckeditor.com/), but can be easily adapted for a variety of setups. An editor bundle is required by CKEditor Cloud Services to enable [document storage](../../guides/collaboration/document-storage.md), [import and export](../../guides/collaboration/import-and-export.md) as well as [connection optimization](../../guides/collaboration/connection-optimization.md) features.

<a id="bundle-requirements">

## Bundle requirements

The editor bundle that can be used by CKEditor Cloud Services must fulfill certain requirements:

* It needs to be built into a single `.js` file.
* The plugins should be added in the [`builtinPlugins`](../../../../ckeditor5/latest/api/module_core_editor_editor-Editor.md#static-member-builtinPlugins) property to be included in the editor bundle. You cannot use the [`config.plugins`](../../../../ckeditor5/latest/api/module_core_editor_editorconfig-EditorConfig.md#member-plugins) option for adding the plugins to the editor.
* The `output.library` property in the webpack configuration should be set to`CKEditorCS` value.
* Plugins used in the editor bundle cannot execute external HTTP requests.
* The editor instance needs to be a default export in the editor bundle.

> **Note**
>
> The officially supported CKEditor 5 plugins already meet the requirement of not executing external requests in case of using them in the editor bundle by CKEditor Cloud Services. If your custom plugins send any external requests, then you have two options:
>
> * If this plugin does not modify the editor’s model or the editor’s output then you can remove it in the editor configuration during the editor bundle upload.
> * If this plugin modifies the editor’s model you can refactor it into 2 plugins. One of them will take care of the editing part, and only the other will make the HTTP requests. Thanks to this you will be able to remove one of the plugins during the editor bundle upload without affecting the output data.
>
> Refer to the [Uploading the editor bundle](../../guides/collaboration/editor-bundle.md#uploading-the-editor-bundle) section for more information on removing the plugins from the editor configuration.

<a id="creating-a-bundle">

## Creating a bundle

The following example acts as a template for how to prepare an editor bundle configuration. It assumes that you have basic CKEditor 5 files - the `package.json`, `main.js`, `index.html` and `style.css` files which are the result of using [CKEditor 5 Builder](https://builder.ckeditor.com/) with “Self-hosted (npm)” integration method.

<a id="dependencies">

### Dependencies

This example uses the following dependencies:

* `ckeditor5`
* `ckeditor5-premium-features`
* `webpack`
* `webpack-cli`
* `mini-css-extract-plugin`

If you followed the Builder setup, `ckeditor5*` packages should be already there. For the rest run:

```bash
npm install -D webpack webpack-cli mini-css-extract-plugin
```

After that, your `package.json` dependencies sections should look as below:

```json
{
  "dependencies": {
	"ckeditor5": "^42.0.0",
	"ckeditor5-premium-features": "^42.0.0"
  },
  "devDependencies": {
	"mini-css-extract-plugin": "^2.9.0",
	"webpack": "^5.92.1",
	"webpack-cli": "^5.1.4"
  }
}
```

<a id="editor-setup">

### Editor setup

It is highly recommended to use the same editor setup for both creating a bundle and integrating CKEditor 5 with the frontend layer. This way, it is easier to keep everything synchronized and consistent between the front end and back end. This requires slight adjustments to the above setup in how parts of the editor are defined and used.

The common parts for both setups are the editor class, list of plugins and editor config. We will split the `main.js` file into 3 files:

* The `main.js` file that will define shared parts.
* The `main-fe.js` file, that will be responsible for editor setup in the frontend application.
* The `main-be.js` file, from which the editor bundle will be generated. It will be bundler entry file.

The first step is adjusting the `main.js` file.

```js
// main.js

import {
	ClassicEditor,
	Essentials,
	Paragraph,
	Bold,
	Italic
} from 'ckeditor5';

import {
	RealTimeCollaborativeEditing
} from 'ckeditor5-premium-features';

// 1. Remove style imports. Those will be used in main-fe.js.
// import 'ckeditor5/ckeditor5.css';
// import 'ckeditor5-premium-features/ckeditor5-premium-features.css';

// import './style.css';

// 2. Create a list of plugins which will be exported.
const pluginList = [
	Essentials,
	Paragraph,
	Bold,
	Italic,
	RealTimeCollaborativeEditing
];

// 3. Adjust 'editorConfig' to utilize 'pluginList'.
const editorConfig = {
	plugins: pluginList,
	...
}

// 4. Export shared parts, instead of initializing editor.
// ClassicEditor.create(document.querySelector('#editor'), editorConfig);

export {
	ClassicEditor,
	pluginList,
	editorConfig
}
```

Next, we will create `main-fe.js` which will be used on the front end.

```js
// main-fe.js

import { ClassicEditor, editorConfig } from './main.js';

// 1. Move style imports from main.js here.
import 'ckeditor5/ckeditor5.css';
import 'ckeditor5-premium-features/ckeditor5-premium-features.css';

import './style.css';

// 2. Initialize editor.
ClassicEditor.create(document.querySelector('#editor'), editorConfig);
```

After that, `main-be.js` file, used for bundling, should be created.

```js
// main-be.js

import { ClassicEditor, pluginList } from './main.js';

class CKEditorCS extends ClassicEditor {}

// 1. Assign plugins to be used in bundle.
CKEditorCS.builtinPlugins = pluginList;

// 2. Export editor class.
export default CKEditorCS;
```

<a id="frontend-building">

### Frontend building

With the above changes, your application can be build and bundled the same way as before. The only adjustment needed is changing the file which is imported.

```html
<!doctype html>
<html lang="en">
	<head>...</head>
	<body>
		...
		<!-- <script type="module" src="./main.js"></script> -->
		<script type="module" src="./main-fe.js"></script>
	</body>
</html>
```

<a id="bundler-configuration">

### Bundler configuration

Next, you need to prepare [webpack](https://webpack.js.org/) configuration to generate a valid bundle.

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

module.exports = {
	entry: './main-be.js', // Your editor build configuration.
	output: {
		filename: 'editor.bundle.js', // Content of this file is required to upload to CKEditor Cloud Services.
		library: 'CKEditorCS', // It is required to expose you editor class under the "CKEditorCS" name!

		libraryTarget: 'umd',
		libraryExport: 'default',
		clean: true
	},
	plugins: [
		new MiniCssExtractPlugin()
	],
	module: {
		parser: {
			javascript: {
				dynamicImportMode: 'eager'
			}
		},
		rules: [
			{
				test: /\.css$/i,
				use: [MiniCssExtractPlugin.loader, 'css-loader']
			}
		]
	},
	performance: { hints: false }
};
```

<a id="generating-the-bundle">

### Generating the bundle

Then, run the following commands inside the CKEditor 5 package folder:

```bash
npx webpack --mode production
```

This command will result in the generation of the `./dist/editor.bundle.js` file. This is the bundle file which then should be [uploaded to CKEditor Cloud Services server](../../guides/collaboration/editor-bundle.md#uploading-the-editor-bundle).

<a id="building-the-editor-bundle-with-typescript">

### Building the editor bundle with TypeScript

The examples presented above are using JavaScript in the editor source files. Building the editor bundle with TypeScript is also possible. To start using TypeScript, follow these steps:

1. Integrate TypeScript with webpack. Refer to [this guide](https://webpack.js.org/guides/typescript/) for more details.
2. Change the extension of the editor source file to `.ts`. Adjust the entry path in `webpack.config.js`.

<a id="creating-a-bundle-from-legacy-configuration">

## Creating a bundle from legacy configuration

> **Warning**
>
> This section relates to bundling editor from [legacy configuration](../../../../ckeditor5/latest/updating/nim-migration/migration-to-new-installation-methods.md#legacy-installation-methods). Legacy configuration is the one using editor presets, DLLs, or `@ckeditor/ckeditor5-*` imports.
>
> Refer to the above [Creating a bundle section](#creating-a-bundle) for up-to-date instructions.

The following example acts as a template for how to prepare an editor build and bundler configuration. It assumes that you have an existing CKEditor 5 package with `ckeditor.js`, `package.json` and `webpack.config.js` files.

<a id="dependencies-2">

### Dependencies

This example uses the following dependencies:

* `@ckeditor/ckeditor5-editor-classic`
* `@ckeditor/ckeditor5-basic-styles`
* `@ckeditor/ckeditor5-essentials`
* `@ckeditor/ckeditor5-paragraph`
* `@ckeditor/ckeditor5-real-time-collaboration`
* `@ckeditor/ckeditor5-dev-utils`
* `@ckeditor/ckeditor5-theme-lark`
* `webpack`
* `webpack-cli`
* `postcss-loader`
* `raw-loader`
* `style-loader`

The `package.json` file dependencies sections should look similar to one below.

> **Warning**
>
> The list of packages and their versions may vary, depending on the editor preset/build and the time when it was created.

```json
{
  "dependencies": {
	"@ckeditor/ckeditor5-basic-styles": "41.4.2",
	"@ckeditor/ckeditor5-editor-classic": "41.4.2",
	"@ckeditor/ckeditor5-essentials": "41.4.2",
	"@ckeditor/ckeditor5-paragraph": "41.4.2",
	"@ckeditor/ckeditor5-real-time-collaboration": "41.4.2"
  },
  "devDependencies": {
	"@ckeditor/ckeditor5-dev-utils": "^32.1.2",
	"@ckeditor/ckeditor5-theme-lark": "41.4.2",
	"postcss-loader": "^4.3.0",
	"raw-loader": "^4.0.2",
	"style-loader": "^2.0.0",
	"webpack": "^5.91.0",
	"webpack-cli": "^4.10.0"
  }
}
```

<a id="editor-build-configuration">

### Editor build configuration

This file presents an example of an editor build configuration. It is used by the bundler as an entry file. This `ckeditorcs.js` file should be created based on your `ckeditor.js` file.

> **Warning**
>
> Remember to adapt the configurations below to your needs because the examples are kept to a minimum.

```js
// ckeditorcs.js

// The editor base creator to use.
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';

// All plugins that you would like to use in your editor.
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import RealTimeCollaborativeEditing from '@ckeditor/ckeditor5-real-time-collaboration/src/realtimecollaborativeediting';

class CKEditorCS extends ClassicEditor {}

// Load all plugins you would like to use in your editor this way.
// This is the only way to load plugins into the editor which will then be used in CKEditor Cloud Services.
CKEditorCS.builtinPlugins = [
	Essentials,
	Paragraph,
	Bold,
	Italic,
	RealTimeCollaborativeEditing
];

// Export your editor.
export default CKEditorCS;
```

<a id="bundler-configuration-2">

### Bundler configuration

This file presents an example of the bundler configuration. Here [webpack](https://webpack.js.org/) is used as the bundler.

```js
// webpack.config.js

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

module.exports = {
	entry: './ckeditorcs.js', // Your editor build configuration.
	output: {
		filename: 'editor.bundle.js', // Content of this file is required to upload to CKEditor Cloud Services.
		library: 'CKEditorCS', // It is required to expose you editor class under the "CKEditorCS" name!

		libraryTarget: 'umd',
		libraryExport: 'default'
	},
	module: {
		parser: {
			javascript: {
				dynamicImportMode: 'eager'
			}
		},
		rules: [
			{
				test: /\.svg$/,
				use: [ 'raw-loader' ]
			},
			{
				test: /\.css$/,
				use: [
					{
						loader: 'style-loader',
						options: {
							injectType: 'singletonStyleTag'
						}
					},
					{
						loader: 'postcss-loader',
						options: styles.getPostCssConfig( {
							themeImporter: {
								themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
							},
							minify: true
						} )
					}
				]
			}
		]
	},
	performance: { hints: false }
};
```

The most important part here is `output.library` field. Without this, the bundle will not work with the CKEditor Cloud Services server.

```js
module.exports = {
	// ...
	output: {
		library: 'CKEditorCS', // It is required to expose you editor class under the "CKEditorCS" name!
		// ...
	},
	// ...
};
```

<a id="generating-the-bundle-2">

### Generating the bundle

Run the following commands inside the CKEditor 5 package folder:

```bash
npm install
npx webpack --mode production
```

This command will result in the generation of the `./dist/editor.bundle.js` file. This is the bundle file which then should be [uploaded to the CKEditor Cloud Services server](../../guides/collaboration/editor-bundle.md#uploading-the-editor-bundle).

> **Note**
>
> Your webpack `output.*` configuration may slightly vary. For CKEditor 5, it is also common to store build artifacts in the `./build/` folder. In such a case, the bundle would be the `./build/editor.bundle.js` file.

<a id="building-the-editor-bundle-with-typescript-2">

### Building the editor bundle with TypeScript

The examples presented above are using JavaScript in the editor source files. Building the editor bundle with TypeScript is also possible. To start using TypeScript, follow these steps:

1. Integrate TypeScript with webpack. Refer to [this guide](https://webpack.js.org/guides/typescript/) for more details.
2. Change the extension of the editor source file to `.ts`. Adjust the entry path in `webpack.config.js`.

---

Full index of the Cloud Services documentation: [llms.txt](../../../llms.txt)
