Sign up (with export icon)

Root types

Contribute to this guideShow the table of contents

In CKEditor 5, a root is the top-level container element in the document model - every editable area has exactly one. The type of that root element determines what content is allowed in that area. By default, roots use the $root model element, which accepts block-level content such as paragraphs, headings, lists, and tables.

You can configure a root to use a different model element via the config.root.modelElement option, and set initial root attributes via config.root.modelAttributes. CKEditor 5 ships with a second built-in root type, $inlineRoot, which restricts the root to inline content only - text and inline formatting, but no block elements. This turns the root into a paragraph-like editing area, suitable for document titles, form labels, meta descriptions, and similar single-line fields. For the technical background behind this feature, see the paragraph-like editor RFC.

Standard root

Copy link

The default root type is $root. It accepts the full range of block-level content: paragraphs, headings, lists, tables, block images, and any other block elements that the enabled plugins support. This is the standard editing experience for most use cases - articles, documents, comments, and similar rich-text areas.

Configuration

Copy link

You do not need to set modelElement explicitly to get this behavior. The following two configurations are equivalent:

ClassicEditor
    .create( {
        attachTo: document.querySelector( '#editor' ),
        root: {
            initialData: '<p>Start writing here.</p>'
        },
        licenseKey: '<YOUR_LICENSE_KEY>',
        // ...
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code
ClassicEditor
    .create( {
        attachTo: document.querySelector( '#editor' ),
        root: {
            initialData: '<p>Start writing here.</p>',
            modelElement: '$root'
        },
        licenseKey: '<YOUR_LICENSE_KEY>',
        // ...
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code

Allowed content in a standard root

Copy link

A standard root accepts whatever block elements the enabled plugins register: paragraphs, headings, lists, tables, block images, code blocks, and similar. Inline content such as text and formatting must appear inside those block elements - it cannot be placed directly in the root. This reflects the standard document structure enforced by the schema: root → blocks → inline content.

Inline root

Copy link

A root configured with $inlineRoot behaves like a single paragraph: pressing Enter has no effect, because inserting a new block is not allowed.

Configuration

Copy link

To configure any single-root editor type as inline-only, set modelElement to '$inlineRoot' in the root config:

import { ClassicEditor, Essentials, Bold, Italic } from 'ckeditor5';

ClassicEditor
	.create( {
		attachTo: document.querySelector( '#editor' ),
		root: {
			initialData: 'My document title',
			modelElement: '$inlineRoot'
		},
		licenseKey: '&lt;YOUR_LICENSE_KEY&gt;',
		plugins: [ Essentials, Bold, Italic ],
		toolbar: [ 'bold', 'italic' ]
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
Copy code

The modelElement option works with all editor types: ClassicEditor, InlineEditor, BalloonEditor, BalloonBlockEditor, DecoupledEditor, and MultiRootEditor.

Allowed content in an inline root

Copy link

The $inlineRoot model element allows the same content as a paragraph: text nodes and inline objects. Block elements are not permitted. Where a standard root follows the root → blocks → inline content structure, an inline root skips the block layer entirely: root → inline content. For a deeper look at how CKEditor 5 schema controls content rules, see the Schema deep dive guide.

Content type Allowed
Plain text Yes
Inline formatting (bold, italic, underline, and similar) Yes
Links Yes
Mentions Yes
Inline images Yes
Paragraphs and headings No
Lists No
Tables No
Block images No

Plugins that only produce block-level output will have no effect inside an $inlineRoot root. You can still include such plugins in your editor setup - they will be inactive when the cursor is inside an inline root, and toolbar items for block-only features will be disabled.

Mixed root types in multi-root editor

Copy link

Mixing root types lets different parts of the same document use different content models. In a multi-root editor, you can configure each root independently. For example, a common pattern is to use an inline root for the title and a standard root for the body:

import { MultiRootEditor, Essentials, Bold, Italic, Paragraph, Heading } from 'ckeditor5';

MultiRootEditor
	.create( {
		roots: {
			title: {
				element: document.querySelector( '#title' ),
				initialData: 'My document title',
				modelElement: '$inlineRoot'
			},
			body: {
				element: document.querySelector( '#body' ),
				initialData: '&lt;p&gt;Main content goes here.&lt;/p&gt;'
			}
		},
		licenseKey: '&lt;YOUR_LICENSE_KEY&gt;',
		plugins: [ Essentials, Bold, Italic, Paragraph, Heading ],
		toolbar: [ 'heading', '|', 'bold', 'italic' ]
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
Copy code

The title root only accepts inline content, while the body root accepts the full range of block elements. The toolbar and undo stack are shared between both roots. See Editor types for a broader overview of the multi-root editor.

Adding roots dynamically

Copy link

In a multi-root editor, you can add roots at runtime using editor.addRoot(). The modelElement option sets the root type, the same way as in the static configuration:

editor.on( 'addRoot', ( evt, root ) => {
    const editableElement = editor.createEditable( root );

    document.querySelector( '#editors' ).appendChild( editableElement );
} );

// Add a standard root.
editor.addRoot( 'section', {
    initialData: '<p>Section content.</p>'
} );

// Add an inline root.
editor.addRoot( 'sectionTitle', {
    modelElement: '$inlineRoot',
    initialData: 'Section title'
} );
Copy code

The root type is fixed at creation time and cannot be changed afterward.

Configuring the host element

Copy link

The root.element option specifies the DOM element the editor uses as the editable area. For non-classic editors, it also accepts a tag name string or an element definition object - letting you control the element’s tag, CSS classes, attributes, and inline styles directly from the editor configuration.

Choosing a semantic element

Copy link

By default, the editor creates a <div> as the editable area. For non-classic editors, consider passing a semantically appropriate element instead. For example, if the inline root serves as a document title, an h1 element is a better fit:

InlineEditor
    .create( {
        root: {
            element: document.querySelector( 'h1#title' ),
            modelElement: '$inlineRoot'
        },
        licenseKey: '<YOUR_LICENSE_KEY>',
        // ...
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code
Note

When using a semantic element such as h1 as the editable area, keep in mind that the .ck-content CSS class applied to all editables defines default font and line-height values that may override the element’s native browser styles. If this affects your layout, scope the relevant CSS variables to that element.

Applying classes and styles

Copy link

Instead of targeting the editable element with a global CSS selector, you can apply CSS classes and inline styles directly from the editor configuration. Pass an element definition object to root.element with name, classes, attributes, and styles fields. The example below adds a custom class and sets a minimum and maximum height on the editable area:

InlineEditor
    .create( {
        root: {
            element: {
                name: 'h1',
                classes: [ 'my-editor' ],
                styles: {
                    'min-height': '300px',
                    'max-height': '500px' // Adds a scrollbar when content overflows.
                }
            },
            initialData: 'My document title',
            modelElement: '$inlineRoot'
        },
        licenseKey: '<YOUR_LICENSE_KEY>',
        // ...
    } )
    .then( /* ... */ )
    .catch( /* ... */ );
Copy code

The classes array and styles object are applied to the editable element. This is useful for controlling dimensions, scoping CSS rules, or integrating with a class-based styling system. For broader CSS customization options, see the Editor and content styles guide.

Styling the editable area

Copy link

Styling the host element

Copy link

When you mount an inline root on a non-block HTML element such as a <span>, the browser may render the editable area with unexpected line breaks or sizing. This happens because block-filler mechanisms used by the editor can interact poorly with inline host elements.

The editor adds the ck-editor__editable_inline-root CSS class to the editable element whenever the root uses $inlineRoot. You can target this class to apply styles specific to inline roots:

.ck-editor__editable_inline-root {
    display: inline-block;
    max-width: fit-content;
    min-width: 50px;
}
Copy code

This ensures the editing area does not collapse or stretch beyond its content. The min-width keeps the editable wide enough to show the placeholder when the root is empty. Mounting on a block element like a <div> does not require any extra CSS.

Note

When using a <span> as the host element, also set display: inline-block on the <span> itself, since block-level children are not valid inside an inline element.