Sign up (with export icon)

AI in multi-root and multi-editor setups

CKEditor AI features support multi-root editors and multiple editors sharing a Context. When your application splits content into separate editing areas – for example, a title and a body – AI Chat, AI Review, and AI Translate operate seamlessly across all of them.

Unlock this feature with selected CKEditor Plans

Try all premium features – no credit card needed.

Sign up for a free trial Select a Plan

Demo

Copy link

The editor below splits content into a title, a description, and a body. AI Chat, AI Review, and AI Translate work across all three.

Title
Description
Body

Supported setups

Copy link
  • Multi-root editor – one MultiRootEditor instance with several editable regions (like a title, a description, a body, and so on). AI features operate across all roots.
  • Multiple editors sharing a ContextContext.create( { /* ... */ } ). AI features operate across the editors in the Context instance.
Experimental

This is an early release of AI support for multi-root and multi-editor setups. Expect improvements and fixes in upcoming releases, which may result in breaking changes.

Per-feature behavior

Copy link
  • AI Chat – the AI reads each root’s content together with its label and description, then decides which root to address based on the user’s prompt.
  • AI Chat history – conversation history is scoped per editor in a Context. Loading a previous conversation maps correctly even if some editors or roots have been destroyed.
  • AI Review and AI Translate – run across all roots of a multi-root editor and across all editors sharing a Context. Suggestions and translations land in the correct root and do not bleed across boundaries. Compatible with real-time collaboration.

Configuration

Copy link

This section shows how to configure CKEditor AI in the two supported setups. The base setup follows the AI integration guide; the snippets below highlight only what differs.

The fields worth pausing on are the root name, label, and description of each root – they look similar but each serves a distinct purpose. The AI uses them differently from assistive technologies, and the way they are declared differs between single-root editors and multi-root editors.

  • Root name – for multi-root editors, the key under config.roots property. It identifies the root in the editor’s model and in collaboration sessions, and must be stable and unique per editor. Single-root editors (ClassicEditor, BalloonEditor, InlineEditor, DecoupledEditor) do not configure a name – their root is internally named main and the name cannot be customized. Root name is used only for content mapping purposes and is not processed by LLM.
  • label – the aria-label of the editable area. Used by the LLM to understand the meaning of particular section. Set it on config.root for single-root editors and on each config.roots.<rootName> entry for multi-root editors. Users can refer particular sections in the prompt – “rewrite the description”, “add a paragraph to the body” – AI will use label to find correct target for the query.
  • description – a short, human-readable description of what the root contains. Provides additional context for particular section beyond label. Treat the description like a short editor tooltip: a sentence that says what the root is for, oriented around its role (“Article body”, “Footnotes panel”), not its position on the page (“Right column”).

Without labels and descriptions, in multi-root or multi-editor setups, the AI cannot reliably distinguish editing areas: a suggested edit may land in the wrong area. The editor logs ai-document-root-missing-label and ai-chat-documents-missing-description warnings when this is detected.

Multi-root editor

Copy link

In a multi-root editor setup, a single editor exposes multiple editing areas. The AI feature plugins load directly on the editor, just like in any single-root setup. Each editing area is then declared under config.roots.<rootName> with its DOM element, initial data, accessible label, and a description used by the AI.

MultiRootEditor
    .create( {
        plugins: [
            AIChat, AIChatHistory, AIChatShortcuts,
            AIQuickActions, AIReviewMode, AITranslate,
            AIEditorIntegration, TrackChanges, /* ... */
        ],
        roots: {
            title: {
                element: document.querySelector( '#title' ),
                initialData: '<h1>...</h1>',
                label: 'Article title',
                description: 'Article title that names the piece for readers.'
            },
            description: {
                element: document.querySelector( '#description' ),
                initialData: '<p>...</p>',
                label: 'Article description',
                description: 'Article description with a short summary of the piece.'
            },
            body: {
                element: document.querySelector( '#body' ),
                initialData: '<p>...</p>',
                label: 'Article body',
                description: 'Main article body — the primary content of the piece.'
            }
        },
        toolbar: [ 'toggleAi', 'aiQuickActions', /* ... */ ],
        ai: {
            container: {
                type: 'sidebar',
                element: document.querySelector( '.ai-sidebar' )
            }
        }

        // ... Other configuration options ...
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code

Multiple editors sharing a Context

Copy link

When several editors share a Context, the AI feature plugins move to the Context-level config.plugins array, so a single Chat, History, Review, and Translate UI is shared across all editors. The editor-integration plugins stay on each editor. The config.ai.container configuration also sits on the Context – the individual editors do not need their own config.ai configuration.

Note

The Context must declare its own config.collaboration.channelId, separate from any channel IDs on the individual editors. AI Chat history is scoped per Context (not per editor), and AI will throw ai-chat-missing-channel-id if the Context has no channel ID configured.

Context
    .create( {
        plugins: [ AIChat, AIChatHistory, AIChatShortcuts, AIReviewMode, AITranslate ],
        ai: {
            container: {
                type: 'sidebar',
                element: document.querySelector( '.ai-sidebar' )
            }
        },
        collaboration: {
            channelId: 'shared-context-channel-id'
        }

        // ... Other configuration options ...
    } )
    .then( context => Promise.all( [
        ClassicEditor.create( {
            context,
            attachTo: document.querySelector( '#article-editor' ),
            plugins: [ AIEditorIntegration, AIQuickActions, TrackChanges, /* ... */ ],
            root: {
                label: 'Article body',
                description: 'Main article body — the primary content of the piece.'
            }
        } ),

        ClassicEditor.create( {
            context,
            attachTo: document.querySelector( '#sidebar-editor' ),
            plugins: [ AIEditorIntegration, AIQuickActions, TrackChanges, /* ... */ ],
            root: {
                label: 'Related links',
                description: 'Sidebar listing articles and resources related to the main piece.'
            }
        } )
    ] ) )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code

Known limitations

Copy link

This is an experimental feature. We will continue to refine the integration in upcoming releases, and more limitations may surface here as early adoption progresses.