The easy questions to answer: Why?
Biggest challenge: Frictionless upgrade path
For Drupal 9 users, the transition must be a simple, painless process to switch from CKEditor 4 to 5 using nothing more than the UI.
For Drupal 10 users, when ckeditor.module
is dropped from Drupal core, the result must be exactly the same as if they used the UI.
In both cases, the automatically generated CKEditor 5 configuration must be:
- Correct (result in a working CKEditor 5 instance)
- Accurate (match the CKEditor 4 configuration as closely as possible, and ensure all HTML tags allowed actually work more about that later)
- Actionable (whenever there is functionality that cannot be migrated automatically, we require precise error messages that allow the user to efficiently investigate potential solutions)
To achieve that, we need a detailed automatic analysis of the “before” (CKEditor 4 + text format configuration) and “after” (CKEditor 5 + text format configuration).
Biggest risk: Data loss
Imagine switching from CKEditor 4 to 5 and noticing that certain content either:
- bad: does not appear anymore in the text editor
- worse: did appear in the text editor, but does not appear anymore in the output after having fixed a typo in CKEditor 5
This is a real risk, because CKEditor 4 and 5 have a different architecture.
CKEditor 4 used browsers’ APIs, so it automatically supported every HTML tag, because browsers support those. This caused cross-browser compatibility issues and maintenance difficulties (since different browsers have different features and bugs).
CKEditor 5 reimplements many low-level APIs in JavaScript. This simplifies their code, makes it easier to test, and makes for a more reliable end result. However, this adds challenges for Drupal because CKEditor 5 does not support every HTML tag by default.
Support for
A few months ago, CKSource released a plugin called
General HTML Support →
. This allows us to let CKEditor 5 render arbitrary HTML, but not to edit arbitrary HTML.
The Drupal CKEditor 5 module uses this to support the use case:
#3216021: Automatically use CKE5's General HTML Support feature on text formats without any TYPE_HTML_RESTRICTOR filter + add `sourceEditing` button →
.
Tags unsupported by CKEditor 5
Drupal allows restricting allowed tags to an arbitrary set of tags, while CKEditor 5 only supports a subset of HTML 5 tags. For example, Drupal allows <dl> <dt> <dd>
by default in , but CKEditor 5 does not support them.
To address this, the CKEditor 5 Drupal module allows configuring additional “manually editable tags” when using the “Source Editing” button (“View Source” in CKEditor 4), so we can track explicitly which tags and attributes are editable only by editing the HTML source. These tags will then work similarly to how they do in CKEditor 4 and Drupal 9 (as they already did not have buttons available in CKEditor 4).
Biggest win: Massively improved toolbar configuration UX
Also see the “Plugin developer DX” section below.
We need excellent user experience when content editors create a new text format and use CKEditor 5, but also when they switch to and modify CKEditor 5’s configuration.
An essential usability feature of CKEditor 4 is that the text format configuration was kept in sync with the CKEditor configuration. CKEditor 4 used
a hidden CKEditor 4 instance in the toolbar configuration UI to get metadata, which then updated filter_html’s “Allowed HTML tags” form field with JavaScript. →
🙃😱 This was brittle and buggy, especially following
#2549077: Allow the "Limit allowed HTML tags" filter to also restrict HTML attributes, and only allow a small whitelist of attributes by default →
. CKEditor 4 also has no protection against enabling filters that conflict with it! Related bug reports:
All of that is solved in CKEditor 5.
As a site builder, you get real-time validation errors:
Biggest change: No Drupal dialogs in CKEditor anymore
There are no longer Drupal-owned dialog forms to add/edit links or images, which is a usability and maintainability improvement, but will require certain contrib modules change their implementation. See:
📌
Deprecate EditorLinkDialog, EditorImageDialog and EditorMediaDialog in Drupal 10.1 for removal in Drupal 11
Fixed
Architecture
Plugin developer DX: less PHP (or even no PHP), more YAML/JS
Plugin definitions require little Drupal/PHP knowledge with clearly delineated ckeditor5
and drupal
sections. There are
helpful messages when the plugin definition does not work →
.
With CKEditor 4, a JavaScript developer would also need to learn a lot about PHP and Drupal just to get their (custom or publicly available) CKEditor plugin working in Drupal. With CKEditor 5 in Drupal, that same JavaScript developer would have to learn almost nothing about Drupal and only would need to rely on CKEditor's API documentation.
This is the most minimal valid CKEditor 5 plugin definition:
MODULE_NAME_marquee:
ckeditor5:
plugins: [PACKAGE.CLASS]
drupal:
label: Marquee
elements:
- <marquee>
Validation constraints for config
The frictionless upgrade path requires metadata about CKEditor 5 plugins (because those are written in JS, and we cannot automatically infer that metadata from PHP code) and their functionality. This metadata is used for validation (for both the upgrade path and the configuration UI), in addition to generating a CKEditor 5 configuration that supports a given set of HTML tags (as mentioned earlier).
This means that the logical way to implement validation is at the configuration entity level. Drupal 8 and 9 core only include limited use of config validation. So this means that CKEditor 5 also has had to solve reusing config schema validation constraints in a form-based UI. See:
✨
[PP-2] Introduce ConfigEntityForm to standardize use of validation constraints for config entities
Active
.
CKEditor 5’s validation constraints fall in two buckets:
Many of these validation constraints are impossible to violate when using the UI, but migrations are risky, and the constraints help ensure that both config exports and the migration path are correct.
Note: The “fundamental compatibility” validation constraint needs to validate fundamental compatibility between an Editor
config entity and its associated FilterFormat
config entity.
These are currently two distinct pieces of configuration (merging them would simplify things a lot:
🌱
[META] Discuss: merge the Editor config entity into the FilterFormat config entity
Active
). Since validation constraints can never span multiple entities, we had to work around that limitation: \Drupal\ckeditor5\Plugin\Editor\CKEditor5::validatePair()
.
Smart Default Settings
When enabled, CKEditor 5 generates smart default settings based on:
- The old CKEditor 4 configuration, if this text format/editor was using it. The toolbar button order and grouping are retained, as is plugin configuration.
- A new group of buttons is added to make the exact HTML restrictions of this text format (
FilterFormatInterface::getHTMLRestrictions()
) match the pre-configured HTML precisely.
So you go from this default “Basic HTML” text format in Drupal 8|9:
To this automatically generated equivalent for CKEditor 5 in Drupal 9|10, with messages explaining what happened:
Toolbar configuration UX
Configuring the CKEditor toolbar must have a simple and fully accessible user experience.
Using the richer available metadata (see the drupal.elements
metadata above!), we can:
- Automatically generate a sensible CKEditor 5 configuration, based on both the old CKEditor 4 configuration (if any) and the set of allowed HTML tags!
- Guarantee that it is a valid configuration and that it works.
(We challenge you to break it: even the “Restricted HTML” text format will give you helpful warnings that you need to remove the the \Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE
type filters for it to be possible to use CKEditor 5).
We also reimplemented the drag-and-drop toolbar configuration UX people used to love for CKEditor 4. Ourc CKEditor 5 implementation of this is less brittle than the jQuery code in core/modules/ckeditor/js/ckeditor.admin.es6.js
, which came with a hidden CKEditor 4 instance to get the necessary metadata. It’s a tiny decoupled JS app.
Dependency evaluation
CKEditor 5 and the required tooling included as part of the integration require a total of 390 packages as a dependency. These are listed on this
file →
.
ckeditor5, ckeditor5-dev-utils
- Maintainership of the package: CKEditor 5 is maintained professionally by CKSource.
- Security policies of the package: Security policies are documented in the repository.
- Expected release and support cycles: Major releases are shipped every couple of months. Minor releases are shipped more frequently. Only one major release is supported at once. Note that CKEditor 5 is not following semver. Their versioning policy mention that minor releases could include "minor" BC breaking changes.
- Code quality: CKEditor 5 code quality is excellent and the project has very good test coverage.
- Other dependencies it would add, if any: many additional dependencies
webpack, webpack-cli, raw-loader, terser-webpack-plugin
- Maintainership of the package
These packages are maintained by the webpack community. Most of the core team members and contributors in the ecosystem do the work in their free time. However, they have pretty solid funding on Open Collective.
- Security policies of the package: Security policies are documented in the Webpack repository.
- Expected release and support cycles: Major releases are happening only every few years. Minor and patch releases are coming at a steady pace. I couldn't find whether Webpack 4.0.0 (the previous major release) is still supported. However, they supported it for at least 6 months since the release of Webpack 5.0.0.
- Code quality: We haven't had the need to evaluate Webpack code base because they have very solid documentation, and it has worked out of the box without any issues for all of our use cases.
- Other dependencies it would add, if any: many additional dev dependencies.
JSDOM
Provides the ability to use Nightwatch for JavaScript unit testing.
-
Maintainership of the package: it is partnered with Tidelift, has multiple maintainers, many of whom are visibly active on the most recent page of commit history. The openbase stats suggest the library is frequently attended. In an informal review of open issues (18% of total issues filed remain open), none of the ones that have been open for more than a few weeks appear pressing. Many of these are feature requests or performance improvements. Issues that require prompt turnaround are attended to properly.
- Security policies of the package: jsdom has Tidelift as their security contact, and reports are made directly to Tidelift. The specifics of Tidelift’s policy cover most Drupal-relevant concerns. The report process is confidential and security releases are carefully coordinated. Security updates are limited to the current version: A maintainer confirmed "No previous releases are supported. As a volunteer project, we only ever support the latest-released version. No backports will be performed."
- Expected release and support cycles: No official release cycles, but commits and releases are frequent (but not unexpectedly so - it’s a familiar frequency for popular modern JS libraries) up to 3 major releases can happen in a year. As confirmed by a project maintainer only the current release is supported.
- Code quality: code is quite readable, eslint ruleset extends a maintainers already extensive config, the test coverage is extensive
- Other dependencies: Yes, but jsdom is itself a dev dependency of Drupal and not present in runtime so the following are listed for reference but not subjected to individual dependency evaluations:
"dependencies": {
"abab": "^2.0.5",
"acorn": "^8.5.0",
"acorn-globals": "^6.0.0",
"cssom": "^0.5.0",
"cssstyle": "^2.3.0",
"data-urls": "^3.0.1",
"decimal.js": "^10.3.1",
"domexception": "^4.0.0",
"escodegen": "^2.0.0",
"form-data": "^4.0.0",
"html-encoding-sniffer": "^3.0.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.0",
"parse5": "6.0.1",
"saxes": "^5.0.1",
"symbol-tree": "^3.2.4",
"tough-cookie": "^4.0.0",
"w3c-hr-time": "^1.0.2",
"w3c-xmlserializer": "^3.0.0",
"webidl-conversions": "^7.0.0",
"whatwg-encoding": "^2.0.0",
"whatwg-mimetype": "^3.0.0",
"whatwg-url": "^10.0.0",
"ws": "^8.2.3",
"xml-name-validator": "^4.0.0"
},
Roadmap to stable
👉
#3238333: Roadmap to CKEditor 5 stable in Drupal 9 →