We are happy to announce the release of CKEditor 5 v49.0.0.
Release highlights
CKEditor 5 v49.0.0 is a major release. It adds shadow DOM support, a refreshed default theme built on design tokens, Trusted Types support, and a redesigned AI Review. It also removes the Watchdog. Before you upgrade, read the v49.0.0 update guide, which also covers the move to ES2023 and native CSS nesting in the distributed stylesheets.
Shadow DOM support
CKEditor 5 can now run inside an open shadow root. Selection, focus, scrolling, drag and drop, and the floating UI (balloons, tooltips, dialogs, and menus) all work there. So do the premium features, including Comments, Track Changes, Real-time Collaboration, and CKEditor AI. Web components, micro-frontends, and design systems built on shadow DOM can now embed the editor without workarounds. Closed shadow roots are not supported.
A shadow root is a separate styling boundary, so load the editor stylesheets into it and override CSS variables on :host instead of :root. The new config.ui.overlayContainer option sets where the floating UI renders. It also helps outside shadow DOM, for example when the editor sits in a container with overflow: hidden. The shadow-aware DOM helpers that the editor uses internally are now public API, so feature and plugin authors can make their own code work in shadow roots. See the Shadow DOM integration guide and, for feature authors, the Shadow DOM deep dive.
Refreshed default theme
The editor ships with a new default theme (#20235). The refresh covers the whole UI, from the toolbar, dropdowns, balloons, dialogs, and forms to the premium features and CKEditor AI. Because it is built on design tokens, matching the editor to your product means overriding a few tokens, and those overrides keep working across editor updates.
The refresh also changes content styles, so published documents look slightly different. Block quotes, code blocks, and horizontal lines use lighter colors, and comment and suggestion markers use new ones.
The new look applies automatically when you upgrade. To keep the previous look, load the legacy theme preset after the editor styles and use the content styles rollback snippets for published content.
Design tokens for theme customization
The theme now uses three layers of design tokens instead of a flat set of CSS variables (#19910): foundation scales (spacing, radius, color, typography), semantic roles shared across components, and per-component tokens. Override a few foundation tokens to align the editor with your design system, one semantic token to restyle a whole class of controls, or a component token such as --ck-button-border-radius to change one component without side effects.
Overrides of legacy variable names keep working. CSS that reads legacy names, such as var(--ck-spacing-small), needs the opt-in aliases. Scoped overrides of foundation tokens no longer reach components, so override the component token for per-element changes. See the theme token naming guide, the theme customization guide, and the refreshed theme and design tokens section of the update guide.
⭐ Redesigned AI Review and more unified AI experience
AI Review has a new design and a Suggest action, which adds an AI change as a Track Changes suggestion instead of applying it directly. While you work through the changes, a balloon in the content follows the current one and a progress bar shows how many are left. AI Review, AI Quick Actions, and Proposed Changes in AI Chat now share the same cards and controls, so actions sit in similar places in all three.
The whole AI interface, including AI Chat, AI Review, Quick Actions, and AI Translate, now uses the refreshed theme and responds to the same token overrides as the rest of the editor. If you customized the AI interface, update your overrides, because legacy AI token names have no fallback. See the AI package section of the theme migration guide for the name mappings.
Trusted Types support
CKEditor 5 now works in applications that enforce Trusted Types with the require-trusted-types-for 'script' Content Security Policy directive (#10845). Before, the browser blocked the HTML strings the editor inserts into the page, so the editor could not run at all. Every place where the editor inserts HTML, including in premium features, now goes through a Trusted Types policy named ckeditor5.
The policy does not sanitize content, so validating the data you load into the editor remains your responsibility. See the Trusted Types section of the CSP guide and the API docs for trustedHtml().
Error handling without the Watchdog
The Watchdog has been removed (along with the @ckeditor/ckeditor5-watchdog package), so CKEditor 5 no longer restarts the editor after an unhandled error. The editor keeps running with its content and undo history, but its state may be inconsistent, so your application decides what to do next: reload the editor, notify the user, or report the error. Register a callback with the new onEditorError() function to learn about errors and which editor or context caused them.
See the Error handling guide and the Migrating from the Watchdog guide for integration and migration instructions.
MAJOR BREAKING CHANGES ℹ️
-
ckeditor5, core, utils: Removed the
@ckeditor/ckeditor5-watchdogpackage and automatic editor restarts after a crash. The editor now retains its content and undo history instead of being recreated from previously saved data.- Removed the
EditorWatchdog,ContextWatchdog, andWatchdogclasses and theWatchdogConfigtype, including their exports fromckeditor5. - Removed the
Editor.EditorWatchdogandEditor.ContextWatchdogstatic fields from every editor class. - Moved
ActionsRecorderto@ckeditor/ckeditor5-corewithout changing its behavior.
Use
onEditorError()to observe errors and identify the editor or context that caused them. It returns a function that unregisters the callback and is also available asEditor.onEditorError()andContext.onEditorError()for framework integrations.import { onEditorError } from 'ckeditor5'; const off = onEditorError( ( { error, source } ) => { console.error( 'An error escaped', source, error ); } );
If you used
ContextWatchdogto share a context between editors, create theContext, pass it in the editor configuration, and destroy it when it is no longer needed.const context = await Context.create( contextConfig ); const editor = await ClassicEditor.create( { context, /* ... */ } );
Integrations that relied on automatic restarts must now handle errors, for example by reloading the editor, notifying the user, or reporting the error to a tracking service.
- Removed the
-
Introduced three layers of CSS custom properties for theme customization, replacing the previous flat set of variables. Closes #19910.
The layers are:
- Foundation primitives, such as the spacing, radius, and color scales.
- Semantic design roles shared across components, such as control padding and surface radius.
- Per-component override points, such as the button or dialog tokens.
Overrides of legacy variables remain supported, except for removed variables, but reading legacy names in custom CSS, such as
var(--ck-spacing-small), requires the opt-in aliases from the migration guide. Scoped overrides of new foundation or semantic tokens do not affect component tokens, so use component tokens for per-element customization. See the theme token naming guide for the layers and recommended override points. -
Introduced a refreshed default theme based on the new design tokens. The new appearance applies to all integrations using the default theme. Closes #20235.
To retain the previous appearance, copy the legacy theme preset from the migration guide into a stylesheet and load it after the editor styles.
The refreshed
.ck-contentstyles also affect published documents, including comment and suggestion markers, block quotes, code blocks, and horizontal lines. Use the rollback snippets to restore the previous content colors.Custom themes can continue to override legacy token names, but CSS that reads those names requires opt-in aliases. See the migration guide for details and exceptions.
-
core: Moved
ActionsRecorderfrom the removed@ckeditor/ckeditor5-watchdogpackage to@ckeditor/ckeditor5-core.Imports from
ckeditor5are unchanged, but direct imports from@ckeditor/ckeditor5-watchdogmust be updated:// Before. import { ActionsRecorder } from '@ckeditor/ckeditor5-watchdog'; // After. import { ActionsRecorder } from '@ckeditor/ckeditor5-core';
The
ActionsRecorderConfig,ActionsRecorderEntry,ActionsRecorderEntryEditorSnapshot,ActionsRecorderErrorCallback,ActionsRecorderFilterCallback, andActionsRecorderMaxEntriesCallbacktypes also moved, andconfig.actionsRecorderretains its typing. -
ui: Replaced
new TooltipManager( editor )withTooltipManager.for( locale )and made the constructor private. Callrelease()instead ofdestroy( editor )when each caller no longer needs the shared instance. All editors still share one instance per page througheditor.ui.tooltipManager, using theLocaleof the first caller. See #3891. -
ui: Renamed
BodyCollection#detachFromDom()toBodyCollection#destroy(), which still destroys the collection's views and removes their container from the DOM. ReplacedetachFromDom()calls withdestroy(), or use the newBodyCollection#unmountFromDom()method to detach the collection without destroying it. See #3891. -
Changed the target for all packages and CDN builds from ES2022 to ES2023.
-
Introduced native CSS nesting in distributed stylesheets in place of flattened selectors. Tools that post-process CKEditor 5 CSS must support nesting or use a nesting transform.
MINOR BREAKING CHANGES ℹ️
-
font, table, ui: Introduced a shared default palette of 120 Material colors for the font and table color features without changing existing document colors. Configure the color options explicitly to retain the previous palettes.
The default color grid now has 12 columns instead of 5, also changing the default value of
fontColor.documentColors, and the newcolorGridColumnsoption controls the grid width in the table and table cell properties balloons. -
block-quote, code-block: Changed the border and background colors of block quotes and code blocks to lighter shades, including in published content. To restore the previous colors, see the content styles rollback snippets in the migration guide. See #19910.
-
export-pdf, export-word: Changed
converterOptions.extra_http_headersto accept an array of{ domain, headers }entries instead of an object keyed by domain.// Before. converterOptions: { extra_http_headers: { 'https://medias.example.org/': { authorization: 'Bearer xxx' } } } // After. converterOptions: { extra_http_headers: [ { domain: 'https://medias.example.org/', headers: { authorization: 'Bearer xxx' } } ] }
This affects only integrations that pass converter options directly to the
exportPdforexportWordcommand, as the object form did not work in the editor configuration. -
comments, track-changes: Changed the default colors of comment highlights and suggestion insertion and deletion markers to match the refreshed theme, including in published
.ck-content. To restore the previous appearance, override the--ck-comment-marker-*and--ck-suggestion-marker-*custom properties as shown in the content styles rollback snippets. -
fullscreen, ui: Added CSS variable declarations on
:hostalongside:rootin editor stylesheets, so overrides on:rootdo not affect editors inside shadow roots. Override variables on the shadow host and declare custom stylesheet variables on both selectors, as described in the "Overriding CSS variables" section of the shadow DOM guide. See #3891.Styles that affect the light DOM, such as
ck-fullscreen-scroll-lockedfor locking page scrolling, are now applied at runtime instead of through theme stylesheets, so custom overrides may require higher specificity. -
ai: Changed
AITabs#containerfrom a plainHTMLElement | nullproperty to an observableHTMLElement | ShadowRoot | nullproperty to support shadow roots.See the documentation for details on running CKEditor AI features inside shadow roots.
-
ai: Removed the
config.ai.assistant.useThemeoption. The AI Assistant now always uses theck-ai-assistant-ui_themeCSS class, which follows the editor theme instead of applying a violet tint. To restore the tint or apply custom colors, see the "Using custom colors for the UI" section of the AI Assistant integration guide. -
ai: Removed the unused
--ck-ai-review-suggestion-active-colorCSS custom property (formerly--ck-color-ai-review-suggestion-active) without a replacement. Remove or replace references to it in custom styles. -
ai: Removed the third
editorparameter fromAIGateway#mergeChangesIntoContent(). -
engine: Removed
ViewRenderer#domDocumentswithout a public replacement, as editing roots can now be inside shadow roots. See #3891. -
fullscreen: Changed the default fullscreen container from
<body>toconfig.ui.overlayContainer, the editor's shadow root, or<body>, in that order of precedence. An explicitconfig.fullscreen.containervalue still takes precedence. See #3891.When the option is not set,
editor.config.get( 'fullscreen.container' )now returnsundefinedinstead of the<body>element.The fullscreen wrapper now uses the
ck-fullscreen__main-wrapper_custom-containerclass when it fills an integrator-provided container, which may require updating custom CSS. -
horizontal-line: Changed the background color of horizontal lines to a lighter shade, including in published content. To restore the previous color, see the content styles rollback snippets in the migration guide. See #19910.
-
mention: Updated the mention suggestion list to match toolbar dropdown lists, with list item buttons and a focus ring instead of a background highlight for the item selected with the keyboard.
Update custom styles to use
ck-mentions__item_focusedon the list item instead ofck-onfor the selected item. Every item, including custom-rendered items, now also uses theck-list-item-buttonclass. -
merge-fields: Updated the merge field suggestion list markup to match the refreshed theme. Custom styles targeting the previous markup may need to be updated. See ckeditor/ckeditor5#20235.
-
ui: Changed the editor's body collection (balloons, dialogs, and tooltips) to attach to the DOM only after the editing root is connected to the document. Each mount target now has its own
.ck-body-wrapper, including shadow roots and configuredconfig.ui.overlayContainercontainers, instead of sharing one wrapper across the page. See #3891.For editors created on detached elements,
.ck-body-wrapperis no longer in the DOM immediately afterEditor.create()resolves. Useeditor.ui.view.body.bodyCollectionContainerinstead ofdocument.querySelector( '.ck-body-wrapper' )to access the container before it is attached. -
ui: Removed the
listenerOptionsoption fromclickOutsideHandler(), so listener priority and capture mode can no longer be configured. Remove this option from calls to the function. See #3891. -
uploadcare: Changed the Uploadcare
uc-configanduc-upload-ctx-providerweb components to render in the editor's floating UI container instead ofdocument.body, enabling shadow DOM support. Continue to access them throughUploadcareEditing#configElementandUploadcareEditing#ctxElement. -
utils: Removed the
getCommonAncestor()DOM utility fromckeditor5-utils, as it did not support traversal across shadow boundaries. To find the lowest common ancestor of two DOM nodes, traverse their ancestors withgetParentNode()and compare the chains, or use the model and viewgetCommonAncestor()methods when working with the editor tree. See #3891. -
utils: Changed
getPositionedAncestor()to returnnullfor elements not connected to a document. Previously, it only required the element to have a parent. See #3891.It also handles these cases differently:
- It now returns
<body>when styles such asposition: relativeortransformmake it the containing block. Previously, it always returnednullfor the main document's<body>. - It now returns
nullinstead of a statically positioned<body>for elements inside an iframe, matching its behavior in the main document. - It now searches the shadow tree for the positioned ancestor of an element assigned to a
<slot>.
- It now returns
Features
-
core, fullscreen, ui: Introduced
config.ui.overlayContainerto specify an element or shadow root for the editor's floating UI, including balloons, dialogs, and tooltips. Load the editor stylesheets into the target container as described in the "Where the floating user interface mounts" section of the shadow DOM guide. See #3891, #5319.The
config.fullscreen.containeroption now also accepts a shadow root. -
ui, utils: Introduced public APIs for shadow DOM integrations:
ShadowRootRegistry,OverlayHost,ShadowSelection,getSelection(),EditorUI#shadowRootRegistry, and theBodyCollectionmounting API. ThegetSelection()utility returns aShadowSelectioninstance, andBodyCollection#attachToDom()now accepts an element or a shadow root. See #3891.Shadow-aware DOM helpers include
getParentNode(),containsNode(),getActiveElement(), andgetElementFromPoint(), with the full list available in theckeditor5-utilsAPI documentation. -
comments, real-time-collaboration: Introduced
config.sidebar.overlayContainerandconfig.presenceList.overlayContainerto specify an element or shadow root for the narrow sidebar annotation balloon and presence list dropdown, respectively. Use these options when a feature runs inside a shadow root or its container clips the floating UI. Otherwise, the features useconfig.ui.overlayContainerwhen configured. -
ckeditor5: Added support for creating editors inside open shadow roots, including selection, focus, positioning, scrolling, drag and drop, floating UI, and premium features. Closed shadow roots are not supported. Closes #3891.
Load editor stylesheets into each shadow root containing editor UI and override CSS variables on the shadow host instead of
:root, as described in the shadow DOM guide. -
ai: Adjusted the content width in AI Quick Actions and AI Chat dialogs to match the text width of the editing area. The dialog width and height can now be customized with CSS custom properties.
-
ai: Added a progress bar to the AI Review panel header showing how many suggestions have been reviewed out of the total.
-
ai: Grouped AI Review sidebar checks into those that run immediately on click and those that open a collapsible options panel before running.
-
ai: Added a confirmation prompt when leaving AI Review or AI Translate with unresolved suggestions to prevent accidental loss of the session.
-
ai: Added support for inserting AI Review changes as Track Changes suggestions instead of applying them directly.
When the
TrackChangesplugin is loaded in every editor in the context, the change balloon and results list include a "Suggest" button that creates suggestions marked as AI-generated and replaces "Accept" if Track Changes mode is enabled in any editor. "Accept all" and "Reject all" remain available in the "Complete" dropdown, with "Accept all" creating suggestions in editors with Track Changes mode enabled and applying changes directly in the others. -
ai: Added a loading skeleton to AI Chat and AI Review during initialization, replacing the empty panel.
-
ai: Added information for the AI agent about the document root's host element, whether the root accepts only inline content, and whether the editor supports soft breaks (Shift+Enter).
-
ai: Replaced the separate "Accept all" and "Exit review" buttons in AI Review with a single "Complete" dropdown, which also introduces a new "Reject all" bulk action. The dropdown is available both in the sidebar header and directly from the suggestion balloon shown in the editor content.
-
ai: Introduced
config.ai.overlayContainerto specify an element or shadow root for AI balloons, dialogs, and dropdowns, as well as the AI interface when using the'overlay'container type.Set this option when the AI interface runs inside a shadow root or its container clips or repositions floating elements. If it is not set,
config.ui.overlayContaineris used when configured. -
ai: Added a full comparison of inserted and removed text for the selected AI Review change, even when "Show changes" is disabled.
Inserted text appears next to struck-through removed text, while other changes remain highlighted.
-
core: Introduced
onEditorError()to observe unhandled editor errors and identify their source editor or context, replacing the removed Watchdog. It returns a function that unregisters the callback. It does not restart editors, save or restore data, or prevent errors from reaching the console. -
ui: Added
TooltipManager#registerBodyCollection( bodyCollection, options )andTooltipManager#unregisterBodyCollection( bodyCollection )to choose the body collection for the shared tooltip balloon. Components outside the editor can now display tooltips in their own DOM tree, including shadow roots, even without an editor on the page. See #3891. -
ui: Introduced reusable UI components for building tabbed interfaces. See #20235.
-
ui: Added the
DropdownView#panelPositionLimiterproperty to constrain automatic dropdown positioning to the visible bounds of a specified element. -
utils: Introduced the
isOfflineutility to check whether the browser is in offline mode. -
Added support for applications that enforce Trusted Types with the
require-trusted-types-for 'script'Content Security Policy directive. Closes #10845.
Bug fixes
- ai: Fixed an issue where AI Chat shortcuts remained in the chat feed after a shortcut was used or a message was sent.
- ai: Fixed inconsistent change numbers between the AI Chat suggestion preview and the chat feed.
- ai: Fixed an issue where AI Translate and AI Review failed when a block element ended with a soft break.
- ai: Fixed the AI model selector to show only recommended models when
ai.models.displayedModelscontains only empty values, such as[ '' ]. Previously, this configuration displayed all available models. - ai: Fixed failures to merge or apply changes through
AIReviewGateway#runReview(),AIReviewGateway#runCustomReview(), andAITranslateGateway#runTranslate()when content contained nested block structures, such as tables or images with captions. - ckbox: Fixed misleading file category or server error messages shown when the internet connection was lost. Users now receive a connection error message.
- comments: Fixed annotation activation when clicking content covered by both a comment and a suggestion. The annotation higher in the sidebar now becomes active, keeping the other annotations in view.
- export-inline-styles: Fixed handling of nested CSS rules in the
stylesheetsandinlineCssconfiguration. Declarations after a nested rule now apply to the parent selector instead of being dropped, and&resolves against each selector in the parent list. Previously, parent selector lists could cause styles to apply to the wrong elements. - image: Fixed the positioning of the text alternative and custom resize balloons to anchor them to the nearest image edge when a centered balloon does not fit. Previously, balloons for left- or right-aligned images could move toward the middle of the editing area instead of staying next to the image.
- pagination: Fixed misalignment between the page break line and its label when scrolling in fullscreen mode.
- track-changes: Fixed an error in the track changes preview and
TrackChangesDatain the classic editor when the element passed toconfig.attachToor as the first argument ofClassicEditor.create()contained suggestions in its HTML. - utils: Fixed an editor crash when passing an empty
translationsconfiguration entry. Closes #20226. - widget: Fixed an issue where clicking beside a block widget that was the only child of a block quote did not change the selection. The click now selects the widget.
Other changes
-
ai, comments, uploadcare: Removed header icons from the AI Assistant, comments archive, and Uploadcare dialogs to match the refreshed theme.
-
ai: Added the editor version to the configuration sent to the AI backend to support compatibility with older editor versions.
-
ai: Changed AI balloons, dialogs, and dropdowns to render in the AI interface's DOM tree instead of
document.bodyto support shadow roots.When
config.ai.container.typeis'custom', setAITabs#containerto the AI interface's host element or useconfig.ai.overlayContainer, which takes precedence. Otherwise, the floating UI falls back todocument.bodyand may appear unstyled inside shadow roots. The'sidebar'and'overlay'container types are unaffected.See the documentation for details on running CKEditor AI features inside shadow roots.
-
ai: Removed normalization of AI-generated content through the editor data pipeline before the AI Review and AI Translate programmatic gateways merge it.
-
emoji: Shortened the emoji search input label from "Find an emoji (min. 2 characters)" to "Find an emoji". A message shown while typing still indicates the minimum character requirement. See #19910.
-
emoji: Updated emoji category buttons to match the tabs in the refreshed theme. See #20235.
-
footnotes: Updated the focus highlight of footnote editing fields and aligned footnote text with its number. See ckeditor/ckeditor5#20235.
-
source-editing-enhanced: Updated the source code editing area's focus highlight and rounded corners to match other text fields. See ckeditor/ckeditor5#20235.
-
table: Aligned controls in equal-width columns in the table and table cell properties forms to match the refreshed theme. See #20235.
-
ui: Removed the header icon from the accessibility help dialog. See #19910.
-
ui: Changed editor UI scrollbar colors to match the theme instead of using browser defaults. See #20235.
-
utils: Added support for resolving points inside shadow roots with
getRangeFromMouseEvent()in Chrome and Edge 128 and later, Firefox 150 and later, and Safari 26.2 and later. Older browsers still return a range beside the shadow host because they do not support theshadowRootsoption ofDocument#caretPositionFromPoint(). See #3891. -
widget: Removed the glossy highlight from buttons for inserting a paragraph next to a widget to match the refreshed theme. See #20235.
Released packages
Check out the Versioning policy guide for more information.
Major releases (contain major breaking changes):
Minor releases (contain minor breaking changes):
Releases containing new features:
Other releases:
Released packages (summary)