Repository navigation
Allow enabling all types of editors on paragraph-like root #762
Description
Developers should be able to initialize the editor on a paragraph-like roots. For example, someone may want to make content of just an <h1> element editable:
<h1 id=editor>Title...</h1>
<script>
InlineEditor.create( document.getElementById( 'editor' ) )
.then( ... )
.catch( ... );
</script>The inline type of editors are most often to be used in such a cases and there's even a ticket for InlineEditor already (https://github.com/ckeditor/ckeditor5-editor-inline/issues/3 and https://github.com/ckeditor/ckeditor5-editor-inline/issues/5). But this requirement applies also to BalloonEditor and I may imagine that someone might want to enable ClassicEditor in such a mode. So, since most of the work is needed in the common part of the code base (core, engine), I'd say that the solution could be generic to all kinds of editors from day one.
In case of editors which upcast existing DOM elements (like inline and balloon editors), the information about the type of element (whether it's div-like or paragraph-like) can be taken from that element itself. But in case of the classic editor it will need to be specified in the config. The config option could work for all editors, overriding the upcasted element.
What's kinda tricky here is mapping DOM element name to schema item name so we can create a model root with the right name. However, I think that this should be enough, taken that the automatic behaviour will be overridable by configuration anyway:
const paragraphLikeElements = [ 'p', 'h1', ... ];
getRootName( domElementName ) {
if ( paragraphLikeElements.includes( domElementName ) {
return '$block';
} else {
return '$root';
}
}Defaulting to the generic $block item for all p/h1/etc elements should be enough.
Add 👍 to this post if you'd like this feature to be implemented.
Activity
Reinmar commentedon Mar 6, 2018
I wrote a few words about how this can be achieved with minimal changes to existing editor creators: https://stackoverflow.com/questions/48896019/ckeditor5-inline-build-puts-a-p-tag-inside-of-an-h1-tag-how-do-i-disable-this/49130552#49130552
I'm curious if it'd work.
claviska commentedon Oct 20, 2018
Here's a fiddle based on that suggestion. It does prevent new paragraphs from being created, but without the appropriate root there's still a child paragraph in the content.
https://jsfiddle.net/v81yndc7/2/
This is a hugely important feature for any app doing serious inline editing. It's not uncommon for headings, paragraphs, and even spans to be made editable.
Reinmar commentedon Feb 11, 2019
We'll need to update https://github.com/ckeditor/ckeditor5/pull/1494/files#r255422201 after p-level editors are a thing.
Reinmar commentedon Sep 5, 2019
The state of things
There's been some work in this area and it looks really promising.
Discussions:
Branches:
Results:
Related tickets:
Plans
Those tests make us believe that there's not that much to do to enable such type of editors. I'm moving this up the waiting list.
The todo list:
$inlineRootis the right direction.$inlineRoot.ange007 commentedon Apr 16, 2020
Are there any movements in this direction?
wc-matteo commentedon Jun 12, 2020
I would be interested in this as well
21 remaining items
bryanjamesmiller commentedon Nov 16, 2023
Thanks for the reply @Witoso , I appreciate the response. If you look at this https://jsfiddle.net/gratiafide/zvaLebx5/ you will see the following problem:

Witoso commentedon Nov 17, 2023
I see, yes, this is what needs to be fixed on our side. I don't see any workarounds that could be applied.
bryanjamesmiller commentedon Nov 17, 2023
@Witoso are there any event hooks that we can use to remove the "p" tags before the HTML gets inserted?
Witoso commentedon Nov 23, 2023
None that I know of. @niegowski?
mlncn commentedon Nov 30, 2023
It looks like the restricted editing feature could be used for some of these use cases.
Make a template that has
<h1>[Title]</h1>and one could in theory count on CKEditor5 producing only that heading with no paragraphs. Some server-side validation would still have to confirm this though, whereas the approach outlined in this issue would be more amenable to simpler validation and sticking the result into an H1.One annoyance is possibly being forced to have placeholder text, though maybe with only one editing option it can be blank and people's cursor would go there automatically. Another likely annoyance is trying to hide the "editing toolbar button" as it is irrelevant with only one editable portion.
Also, regarding this issue not getting a lot of upvotes— it is really hard to find. This use case comes up anytime one has structured data (so, at least, it comes up a lot in Drupal and some other more powerful/complex content management systems).
bryanjamesmiller commentedon Jan 23, 2024
@mlncn it seems like a pretty common issue to me as a basic install like this will cause it - https://jsfiddle.net/gratiafide/zvaLebx5/ this should be fixed as it's a major problem for some of us - #762 (comment)
mlncn commentedon Jan 24, 2024
Oh i absolutely agree.
My current workaround in Drupal is to make a text format that only allows paragraph tags (no other block elements like titles) and then strip those paragraph tags out with a module we made just to deal with CKEditor5's recalcitrance here: Strip paragraph tags filter.
FLEdev commentedon Feb 26, 2024
I am integrating a audio / video extension within Drupal CMS and CKeditor5. After defining the schema and using the proposed definitions, everytime there is a
wrapper tag around those elements:
Is there any way to avoid this? The Proposed .createRoot is not available at the upcast model wrapper - using:schema.register('cke5MediaAudioWrapper', {
allowWhere: "$block",
isLimit: true,
isObject: true,
isBlock: true,
isSelectable: false,
allowChildren: ['cke5MediaAudio', 'cke5MediaAudioPreview'],
allowAttributes: [
'class',
'data-settings'
],
});
// or:
conversion.for("upcast").elementToElement({
view: {
name: 'audio',
attributes: [
'data-media-name',
'autoplay',
'loop',
'name',
'preload',
'src'
]
},
//converterPriority: "high",
model: ( viewElement, { writer } ) => {
const entityAttributes = {
'data-media-name': viewElement.getAttribute('data-media-name'),
'autoplay': viewElement.getAttribute('autoplay'),
'data-type': viewElement.getAttribute('data-type'),
'loop': viewElement.getAttribute('loop'),
'name': viewElement.getAttribute('name'),
'preload': viewElement.getAttribute('preload'),
'src': viewElement.getAttribute('src'),
};
return writer.createElement( 'cke5MediaAudio', entityAttributes);
}
});
FLEdev commentedon Feb 26, 2024
@Witoso : any idea how to avoid adding "p" tags into "non wrapping" elements like "audio/video" or generally how to avoid such behaviour?
As far I understand, there is no preprocess function/hook to change this. Also the "autoParagraph" setting is not available (see: #762).
Reinmar commentedon Apr 10, 2026
Just FYI, we're working in several sub-initiatives to fully enable paragraph-like and even span-like editors.
The most recent work that's happening is:
config.root.modelElement. #20028andrzejkala commentedon Jun 11, 2026
This one has been on the radar since 2018, with 44 👍 and a steady stream of "we need this too" from people building CMS fields, layout builders, and templated editing surfaces.
Thank you for keeping it alive and for the detailed real-world use cases along the way - they shaped where this landed. 🙌
With the last two releases of CKEditor 5 we can finally say that it’s now possible to enable all types of editors on a paragraph-like root 🎉
For the details, see the documentation on setting up Inline roots.
Below is a per-release breakdown of this feature's development.
v48.1.0
This release introduced the model side.
config.root.modelElement(single-root) andconfig.roots.<name>.modelElement(multi-root) choose the schema item for a root, defaulting to$root, with a new generic$inlineRootitem for inline-only content andmodelAttributesavailable on all editor types - classic, inline, balloon, decoupled, and multi-root.Closed issues
v48.2.0
Second release added the view side, release-noted as "Editor roots on paragraph-like elements."
config.root.element/config.roots.<name>.elementnow accept a tag-name string like'h1'or aViewRootElementDefinition(tag, CSS classes, inline styles, attributes), so a root can be created as a heading, paragraph, or custom block without an existing DOM node (<textarea>and<input>excluded).We’ve also made sure that our built-in features understand the concept of inline roots and work with them seamlessly. The only outlier at the moment are the images, mentioned in the Known issues section below
Closed issues
RootConfig#element#20047Known issues
Inline images are temporarily disabled inside inline-only roots; proper support for inserting and managing them there is still being implemented.
Additional Links
RootConfigAPI —modelElement,element,modelAttributesRFC’s