Skip to content

Getting started ​

CKEditor 5 Elements is element first: the content is a tree of named elements, each with its attributes, and every feature acts on an element.

  • The content is elements. Blocks, containers, objects and inline elements, formatting included: a bold is an element, as a paragraph is. Each one has a name, attributes of its own, and the place in the tree that the element schema gives it.
  • The features act on elements. A command is for an element; a menu is the menu of an element; the clipboard copies whole elements; the breadcrumbs show the path of elements at the cursor; an element is selected as a whole; quick insert inserts the elements allowed at a place.
  • The data is the model, element for element. The converters of the schema write each element with its name and its attributes, and read the same data back.

It is one package, ckeditor5-elements. Install it next to ckeditor5:

sh
npm install ckeditor5 ckeditor5-elements

The version of ckeditor5-elements follows the major version of CKEditor: 49.x works with ckeditor5 49.x (the peer dependency is ^49.0.0). A new CKEditor major version needs a new release.

Import the features from ckeditor5-elements, and its stylesheet, ckeditor5-elements/index.css, which styles all of them:

ts
import { ClassicEditor, Essentials, Paragraph } from 'ckeditor5';
import { ElementClipboard, ElementContextMenu } from 'ckeditor5-elements';

import 'ckeditor5/ckeditor5.css';
import 'ckeditor5-elements/index.css';

await ClassicEditor.create({
	attachTo: document.querySelector('#editor'),
	licenseKey: 'GPL',
	plugins: [Essentials, Paragraph, ElementContextMenu, ElementClipboard],
	elementContextMenu: (element, editor) => [editor.commands.get('copyElement'), editor.commands.get('cutElement')]
});
FeatureWhat it addsPackage
CommandsCommands that act on an element, and their buttonsckeditor5-elements-core
ActionsElementActions: move an element up or down, duplicate or delete itckeditor5-elements-actions
ClipboardElementClipboard: copy, cut and paste whole elementsckeditor5-elements-actions
AttributesElementAttributes: show the attributes of an element in a modalckeditor5-elements-attributes
Context menuElementContextMenu: a menu of commands for the right-clicked elementckeditor5-elements-menu
BreadcrumbsElementBreadcrumbs: the element path at the cursorckeditor5-elements-menu
Quick insertElementQuickInsert: search and insert an element allowed at a positionckeditor5-elements-menu
Selectable elementsElementSelectable: select an element as a whole, by a click or keysckeditor5-elements-interaction
Clickable elementsElementClickable: a callback when an element is clickedckeditor5-elements-interaction
Element toolbarElementToolbar: floating toolbars for elements, by name or filterckeditor5-elements-toolbar
Element schemaElementSchema: declare the elements of the model from a JSON fileckeditor5-elements-schema
Schema commandsElementSchemaCommands: commands and buttons for themckeditor5-elements-schema

Load only the plugins you use: the others are not part of your build.

One package

Like ckeditor5, ckeditor5-elements brings its features in packages of its own, in the same version: the packages in the table above, which say where a feature is. Do not install or import them yourself: two versions of them would be loaded, which fails. The menus of the context menu, the breadcrumbs and quick insert are shown by one plugin, ElementMenu, which each of them loads.

See it all at work

The full example is an editor with all the features together, and the DocBook XML and DITA XML editors are WYSIWYG editors of XML documents. Its source is in the ckeditor5-elements-example package; run it with pnpm example.

All the examples in this guide are live editors, and their source is shown below them. Each one has a Source button (CKEditor's SourceEditing): it shows the data of the editor, the elements with their attributes as they are saved, and loads it back when you edit it. They use the note element (<aside class="note">) of the full example:

note.ts
ts
import { Plugin, type Editor } from 'ckeditor5';
import { ElementButtonView, ElementContainerCommand } from 'ckeditor5-elements';

const noteIcon =
	'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">' +
	'<path d="M4 3h12v14H4zm1.5 1.5v11h9v-11zM7 7h6v1.5H7zm0 3h6v1.5H7z"/></svg>';

const warningIcon =
	'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="' +
	'M10 2.2 18.6 17H1.4zm0 3L4 15.5h12zM9.25 8h1.5v4h-1.5zm0 5h1.5v1.5h-1.5z"/></svg>';

/**
 * Wraps blocks with a note, or unwraps the note they are in. With a `type`, only notes of that type: an
 * `ElementCommand` matches an element by its name and attributes.
 */
export class NoteCommand extends ElementContainerCommand {
	constructor(editor: Editor, type?: string) {
		super(editor, 'note');

		if (type) {
			this.elementAttributes = { type };
		}
	}
}

/**
 * A note: an element that holds blocks, `<aside class="note">`. A warning is a note with the `warning` type,
 * `<aside class="note warning">`.
 *
 * The `note` command toggles a note (any note is "on" for it), and the `warning` command toggles a warning. Their
 * toolbar buttons have the same names.
 */
export class Note extends Plugin {
	public static get pluginName(): 'Note' {
		return 'Note' as const;
	}

	public init(): void {
		const editor = this.editor;
		const t = editor.t;

		editor.model.schema.register('note', {
			allowWhere: '$block',
			allowContentOf: '$root',
			allowAttributes: ['type']
		});
		editor.conversion.elementToElement({ model: 'note', view: { name: 'aside', classes: 'note' } });
		editor.conversion.attributeToAttribute({
			model: { name: 'note', key: 'type', values: ['warning'] },
			view: { warning: { key: 'class', value: 'warning' } }
		});

		this._addCommand('note', new NoteCommand(editor), t('Note'), noteIcon);
		this._addCommand('warning', new NoteCommand(editor, 'warning'), t('Warning'), warningIcon);
	}

	private _addCommand(name: 'note' | 'warning', command: NoteCommand, title: string, icon: string): void {
		const editor = this.editor;

		command.set({ title, icon });
		editor.commands.add(name, command);
		editor.ui.componentFactory.add(name, locale => new ElementButtonView(locale, command));
	}
}

declare module 'ckeditor5' {
	interface PluginsMap {
		[Note.pluginName]: Note;
	}

	interface CommandsMap {
		note: NoteCommand;
		warning: NoteCommand;
	}
}