The CKEditor 5 core framework provides the fundamental building blocks for the editor architecture, including the plugin system, command pattern, configuration management, and editor lifecycle.
The plugin system is the foundation of CKEditor 5's modular architecture. All features are implemented as plugins that can be easily combined and configured.
/**
* Base class for all CKEditor 5 plugins
*/
abstract class Plugin {
/**
* The editor instance that this plugin belongs to
*/
readonly editor: Editor;
/**
* The plugin name for identification and dependency resolution
*/
static readonly pluginName?: string;
/**
* Array of plugin dependencies that must be loaded before this plugin
*/
static readonly requires?: PluginConstructor[];
/**
* Indicates if this is an official CKEditor plugin
*/
static readonly isOfficialPlugin?: boolean;
/**
* Creates a plugin instance
* @param editor - The editor instance
*/
constructor(editor: Editor);
/**
* Initializes the plugin - called during editor initialization
*/
init?(): void | Promise<void>;
/**
* Called after all plugins have been initialized
*/
afterInit?(): void | Promise<void>;
/**
* Destroys the plugin - called during editor destruction
*/
destroy?(): void | Promise<void>;
}
/**
* Plugin constructor type
*/
type PluginConstructor = new (editor: Editor) => Plugin;
/**
* Plugin collection managing all loaded plugins
*/
interface PluginCollection {
/**
* Gets a plugin instance by name or constructor
*/
get<T extends Plugin>(key: string | PluginConstructor<T>): T;
/**
* Checks if a plugin is loaded
*/
has(key: string | PluginConstructor): boolean;
/**
* Iterator over all loaded plugins
*/
[Symbol.iterator](): Iterator<[PluginConstructor, Plugin]>;
}Usage Example:
import { Plugin, Command } from 'ckeditor5';
// Custom plugin example
class MyCustomPlugin extends Plugin {
static get pluginName() {
return 'MyCustomPlugin';
}
static get requires() {
return ['Paragraph'];
}
init() {
console.log('MyCustomPlugin initialized');
// Register a command
this.editor.commands.add('myCommand', new MyCustomCommand(this.editor));
}
}
// Using the plugin
ClassicEditor
.create(element, {
plugins: [MyCustomPlugin],
// ... other config
});Commands encapsulate editor operations and provide a consistent way to execute actions, manage state, and handle undo/redo.
/**
* Base class for all CKEditor 5 commands
*/
abstract class Command {
/**
* The editor instance that this command belongs to
*/
readonly editor: Editor;
/**
* Current value of the command (e.g., boolean for toggle commands)
*/
value: unknown;
/**
* Whether the command is enabled and can be executed
*/
isEnabled: boolean;
/**
* Creates a command instance
* @param editor - The editor instance
*/
constructor(editor: Editor);
/**
* Executes the command with given parameters
* @param args - Command arguments
*/
abstract execute(...args: any[]): void;
/**
* Refreshes the command state (value and isEnabled)
* Called automatically when model changes
*/
refresh(): void;
/**
* Destroys the command
*/
destroy(): void;
}
/**
* Collection of commands available in the editor
*/
interface CommandCollection {
/**
* Adds a command to the collection
* @param commandName - Name of the command
* @param command - Command instance
*/
add(commandName: string, command: Command): void;
/**
* Gets a command by name
* @param commandName - Name of the command
* @returns Command instance or undefined
*/
get(commandName: string): Command | undefined;
/**
* Checks if a command exists
* @param commandName - Name of the command
* @returns True if command exists
*/
has(commandName: string): boolean;
/**
* Executes a command
* @param commandName - Name of the command
* @param args - Command arguments
* @returns Command execution result
*/
execute(commandName: string, ...args: any[]): any;
/**
* Removes a command from the collection
* @param commandName - Name of the command
*/
delete(commandName: string): boolean;
/**
* Iterator over command names
*/
names(): IterableIterator<string>;
/**
* Iterator over commands
*/
commands(): IterableIterator<Command>;
}
/**
* Multi-command that can execute multiple commands at once
*/
class MultiCommand extends Command {
/**
* Registers a child command
* @param commandName - Name of the command to register
*/
registerChildCommand(commandName: string): void;
/**
* Executes the first enabled child command
* @param args - Command arguments
*/
execute(...args: any[]): void;
}Usage Example:
import { Command } from 'ckeditor5';
// Custom command example
class MyToggleCommand extends Command {
refresh() {
const selection = this.editor.model.document.selection;
const element = selection.getFirstPosition().parent;
this.isEnabled = this.editor.model.schema.checkAttribute(element, 'myAttribute');
this.value = element.hasAttribute('myAttribute');
}
execute() {
const model = this.editor.model;
const selection = model.document.selection;
model.change(writer => {
const element = selection.getFirstPosition().parent;
if (this.value) {
writer.removeAttribute('myAttribute', element);
} else {
writer.setAttribute('myAttribute', true, element);
}
});
}
}
// Register and use the command
editor.commands.add('myToggle', new MyToggleCommand(editor));
editor.execute('myToggle');Context provides shared functionality for multiple editor instances, useful for collaborative editing and shared resources.
/**
* Context provides shared functionality for multiple editor instances
*/
class Context {
/**
* Collection of context plugins
*/
readonly plugins: PluginCollection;
/**
* Configuration object
*/
readonly config: Config;
/**
* Locale object for internationalization
*/
readonly locale: Locale;
/**
* Creates a context instance
* @param config - Context configuration
*/
static create(config?: ContextConfig): Promise<Context>;
/**
* Destroys the context
*/
destroy(): Promise<void>;
}
/**
* Base class for context plugins
*/
abstract class ContextPlugin {
/**
* The context instance
*/
readonly context: Context;
/**
* Plugin name for identification
*/
static readonly pluginName?: string;
/**
* Plugin dependencies
*/
static readonly requires?: ContextPluginConstructor[];
/**
* Creates a context plugin instance
* @param context - The context instance
*/
constructor(context: Context);
/**
* Initializes the plugin
*/
init?(): void | Promise<void>;
/**
* Called after all plugins are initialized
*/
afterInit?(): void | Promise<void>;
/**
* Destroys the plugin
*/
destroy?(): void | Promise<void>;
}
/**
* Context configuration interface
*/
interface ContextConfig {
/**
* Array of context plugins to load
*/
plugins?: ContextPluginConstructor[];
/**
* Language configuration
*/
language?: LanguageConfig;
/**
* Additional plugin configurations
*/
[pluginName: string]: any;
}Centralized configuration system with type safety and default value management.
/**
* Configuration object managing editor settings
*/
class Config {
/**
* Gets a configuration value
* @param name - Configuration key (supports dot notation)
* @returns Configuration value
*/
get(name: string): unknown;
/**
* Sets a configuration value
* @param name - Configuration key or object with multiple values
* @param value - Value to set (ignored if name is object)
*/
set(name: string | object, value?: unknown): void;
/**
* Defines a configuration value with default
* @param name - Configuration key or object with multiple defaults
* @param value - Default value (ignored if name is object)
*/
define(name: string | object, value?: unknown): void;
/**
* Iterator over configuration keys
*/
names(): IterableIterator<string>;
}System for managing keyboard shortcuts and editor-specific key bindings.
/**
* Keystroke handler for editing context
*/
class EditingKeystrokeHandler {
/**
* Sets a keystroke handler
* @param keystroke - Keystroke pattern (e.g., 'Ctrl+B')
* @param callback - Handler function or command name
* @param options - Handler options
*/
set(
keystroke: string | string[],
callback: EditingKeystrokeCallback | string,
options?: {
priority?: 'highest' | 'high' | 'normal' | 'low' | 'lowest';
}
): void;
/**
* Destroys the keystroke handler
*/
destroy(): void;
}
/**
* Callback function for keystroke handling
*/
type EditingKeystrokeCallback = (
keyEvtData: KeyboardEvent,
cancel: () => void
) => void;System for managing asynchronous operations and preventing editor destruction during ongoing processes.
/**
* Manages pending actions that prevent editor destruction
*/
class PendingActions {
/**
* Whether there are any pending actions
*/
readonly hasAny: boolean;
/**
* First pending action in the queue
*/
readonly first: PendingAction | null;
/**
* Adds a pending action
* @param message - Human-readable action description
* @param action - Action object or promise
* @returns Added pending action
*/
add(message: string, action?: Promise<unknown> | object): PendingAction;
/**
* Removes a pending action
* @param action - Action to remove
*/
remove(action: PendingAction): void;
/**
* Iterator over pending actions
*/
[Symbol.iterator](): Iterator<PendingAction>;
}
/**
* Represents a single pending action
*/
interface PendingAction {
/**
* Human-readable action description
*/
readonly message: string;
/**
* Action object or promise
*/
readonly action?: Promise<unknown> | object;
}Usage Example:
// Add a pending action
const pendingAction = editor.plugins.get('PendingActions').add('Saving document...');
// Simulate async operation
setTimeout(() => {
// Remove the pending action when done
editor.plugins.get('PendingActions').remove(pendingAction);
}, 2000);