NEWCKEditor AI on your premises: Hook your LLM and register MCP tools. Webinar coming soon!
Sign up (with export icon)

Understand the generated build output

Contribute to this guideShow the table of contents

Running npm run build creates two flavors of output in dist/: one for npm-based installations and one for direct browser loading. You can use the same generated package together with the standard CKEditor 5 npm, ZIP, or CDN distributions.

This guide uses <packageName> as the npm package placeholder. Callout and CKCallout are example plugin and UMD global names for ckeditor5-callout. Your actual package, plugin, and global names depend on the values you used during generation.

Generated project structure

Copy link

The generated package has a small, predictable structure:

Path Purpose
src/ Plugin source files and the package entry point.
sample/ The local sample app loaded by npm run start.
tests/ Unit tests run by Vitest.
theme/ Icons and CSS used by your plugin.
lang/ Translation context and generated *.po files.
scripts/ Helper scripts, including translation synchronization.
dist/ Files created by npm run build.
ckeditor5-metadata.json Plugin metadata used by CKEditor 5 tools.
vite.config.[js|ts] Build and test configuration.
src/augmentation.ts, typings/, tsconfig*.json TypeScript-only typing support.

TypeScript augmentation

Copy link

When you choose the TypeScript template, the generator creates the src/augmentation.ts file. This is the place where the generated package augments CKEditor 5 types such as PluginsMap. Update this file when you add new plugins or commands.

Depending on your plugin, you will usually augment one or more of these interfaces:

  • EditorConfig when your plugin adds configuration.
  • PluginsMap when you want editor.plugins.get() to return your plugin type.
  • CommandsMap when you add commands and want editor.commands.get() to return typed results.

The generated template already imports ./augmentation.js from src/index.ts.

import type { Callout } from './index.js';

declare module '@ckeditor/ckeditor5-core' {
    interface PluginsMap {
        [ Callout.pluginName ]: Callout;
    }
}
Copy code

What goes into dist/

Copy link

After npm run build, the output looks similar to this:

dist/
├─ index.js
├─ index.css
├─ index.d.ts                # TypeScript only
├─ callout.d.ts              # TypeScript only
├─ augmentation.d.ts         # TypeScript only, if present
└─ browser/
    ├─ index.es.js
    ├─ index.umd.js
    └─ index.css
Copy code

Each file has a different job:

File Use it for
dist/index.js The npm package entry. It is ESM and keeps ckeditor5 as an external dependency.
dist/index.css The CSS file for npm consumers. Import it separately in the consuming app.
dist/*.d.ts TypeScript declarations generated from src/. They are published together with the npm build.
dist/browser/index.es.js The browser ESM build for type="module" and import-map setups. It still expects ckeditor5 to be provided separately.
dist/browser/index.umd.js The browser UMD build for plain <script> setups. It expects CKEDITOR to exist and exposes your package on the global name chosen for the build.
dist/browser/index.css The CSS file for ZIP and CDN-style browser integrations.

package.json is already configured so that publishing the package ships dist/ and ckeditor5-metadata.json.

What you do not get from the generator:

  • No full editor bundle with CKEditor 5 built in.
  • No ZIP archive ready to download.
  • No automatic CDN hosting.

Use the output with npm

Copy link

Start from the npm or ZIP quick start guide, then add your generated package on top.

The npm build is the right choice when your application already uses a package manager and a bundler. Publish the package to npm, install it from a local path, or use it from a workspace. In every case, the consuming project loads the package root, not dist/browser/.

npm install ckeditor5 <packageName>
Copy code
import { ClassicEditor, Essentials, Paragraph } from 'ckeditor5';
import { Callout } from '<packageName>';

import 'ckeditor5/ckeditor5.css';
import '<packageName>/index.css';

ClassicEditor
    .create( {
        attachTo: document.querySelector( '#editor' ),
        root: {
            initialData: '<p>Hello from CKEditor 5!</p>'
        },
        licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
        plugins: [ Essentials, Paragraph, Callout ],
        toolbar: [ 'undo', 'redo', '|', 'callout' ]
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code

TypeScript consumers automatically pick up the generated declaration files through the package types entry.

Use the output with a ZIP-based setup

Copy link

Start from the npm or ZIP quick start guide, then copy dist/browser/ to the same static assets area where you keep the extracted CKEditor 5 ZIP files.

Copy link

Use dist/browser/index.es.js when your page loads CKEditor 5 from ESM files and import maps:

<div id="editor"></div>

<link rel="stylesheet" href="./vendor/ckeditor5/ckeditor5.css" />
<link rel="stylesheet" href="./vendor/callout/index.css" />

<script type="importmap">
    {
        "imports": {
            "ckeditor5": "./vendor/ckeditor5/ckeditor5.js",
            "ckeditor5/": "./vendor/ckeditor5/",
            "<packageName>": "./vendor/callout/index.es.js"
        }
    }
</script>

<script type="module">
    import { ClassicEditor, Essentials, Paragraph } from 'ckeditor5';
    import { Callout } from '<packageName>';

    ClassicEditor
        .create( {
            attachTo: document.querySelector( '#editor' ),
            root: {
                initialData: '<p>Hello from CKEditor 5!</p>'
            },
            licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
            plugins: [ Essentials, Paragraph, Callout ],
            toolbar: [ 'undo', 'redo', '|', 'callout' ]
        } )
        .then( /* ... */ )
        .catch( /* ... */ );
</script>
Copy code

Alternative: UMD with script tags

Copy link

Use dist/browser/index.umd.js when your page uses the UMD files from the ZIP package:

<div id="editor"></div>

<link rel="stylesheet" href="./vendor/ckeditor5/ckeditor5.css" />
<link rel="stylesheet" href="./vendor/callout/index.css" />

<script src="./vendor/ckeditor5/ckeditor5.umd.js"></script>
<script src="./vendor/callout/index.umd.js"></script>

<script>
    const { ClassicEditor, Essentials, Paragraph } = CKEDITOR;
    const { Callout } = CKCallout;

    ClassicEditor
        .create( {
            attachTo: document.querySelector( '#editor' ),
            root: {
                initialData: '<p>Hello from CKEditor 5!</p>'
            },
            licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
            plugins: [ Essentials, Paragraph, Callout ],
            toolbar: [ 'undo', 'redo', '|', 'callout' ]
        } )
        .then( /* ... */ )
        .catch( /* ... */ );
</script>
Copy code

CKCallout is the example UMD global. If your package is ckeditor5-callout, this is the suggested default name. Otherwise, replace it with the global name you confirmed during generation.

Use the output with CDN or cloud setups

Copy link

Start from the CDN quick start guide, then host your generated browser files yourself. CKEditor 5 can come from the CDN, while your plugin still comes from your server.

Copy link
<div id="editor"></div>

<link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/48.0.0/ckeditor5.css" />
<link rel="stylesheet" href="/plugins/callout/index.css" />

<script type="importmap">
    {
        "imports": {
            "ckeditor5": "https://cdn.ckeditor.com/ckeditor5/48.0.0/ckeditor5.js",
            "ckeditor5/": "https://cdn.ckeditor.com/ckeditor5/48.0.0/",
            "<packageName>": "/plugins/callout/index.es.js"
        }
    }
</script>

<script type="module">
    import { ClassicEditor, Essentials, Paragraph } from 'ckeditor5';
    import { Callout } from '<packageName>';

    ClassicEditor
        .create( {
            attachTo: document.querySelector( '#editor' ),
            root: {
                initialData: '<p>Hello from CKEditor 5!</p>'
            },
            licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
            plugins: [ Essentials, Paragraph, Callout ],
            toolbar: [ 'undo', 'redo', '|', 'callout' ]
        } )
        .then( /* ... */ )
        .catch( /* ... */ );
</script>
Copy code
Tip

If your application also uses Vite, extend the externalization setup from the CDN quick start guide so Vite also leaves your custom package import unresolved.

Alternative: UMD globals

Copy link
<div id="editor"></div>

<link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/48.0.0/ckeditor5.css" />
<link rel="stylesheet" href="/plugins/callout/index.css" />

<script src="https://cdn.ckeditor.com/ckeditor5/48.0.0/ckeditor5.umd.js"></script>
<script src="/plugins/callout/index.umd.js"></script>

<script>
    const { ClassicEditor, Essentials, Paragraph } = CKEDITOR;
    const { Callout } = CKCallout;

    ClassicEditor
        .create( {
            attachTo: document.querySelector( '#editor' ),
            root: {
                initialData: '<p>Hello from CKEditor 5!</p>'
            },
            licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
            plugins: [ Essentials, Paragraph, Callout ],
            toolbar: [ 'undo', 'redo', '|', 'callout' ]
        } )
        .then( /* ... */ )
        .catch( /* ... */ );
</script>
Copy code

Choose the right output quickly

Copy link
If your project uses… Use these generated files
npm and a bundler dist/index.js and dist/index.css
ZIP files and import maps dist/browser/index.es.js and dist/browser/index.css
ZIP files and plain <script> tags dist/browser/index.umd.js and dist/browser/index.css
CKEditor Cloud CDN plus your static hosting dist/browser/index.es.js or dist/browser/index.umd.js, plus dist/browser/index.css