Problem Statement
In the (legacy) Block Editor, hard breaks (line breaks created with Shift + Enter within a paragraph) are silently erased when content is re-opened for editing. The content is saved correctly to disk, but when the author opens the field again in the editor, every hardBreak is gone and the text collapses onto a single line. If the author then saves without manually re-adding every line break, the content is persisted incorrectly.
Example: an address laid out on multiple lines within one paragraph (using Shift+Enter) appears on a single line the next time the contentlet is edited.
Impact: Silent data loss / formatting loss on edit. Authors must remember to re-insert every line break before every save, which is error-prone. Reported by a customer.
Browser/OS: Not browser-specific — defect is in the editor's content-load logic.
Root cause (from code investigation)
core-web/libs/block-editor/src/lib/shared/utils/parser.utils.ts:35:
const basicNodes: BlockMap = { paragrah: true, text: true, doc: true };
When a Block Editor field has allowed-block restrictions (allowedBlocks.length > 1), dot-block-editor.component.ts (setEditorJSONContent, ~line 707) runs the loaded JSON through removeInvalidNodes → purifyNodeTree, which drops any node whose type is not in the allowed map. hardBreak is never present in basicNodes nor in relatedContent, so every hardBreak node is stripped on load.
Notes:
- Only triggers for fields configured with block restrictions (
allowedBlocks.length > 1); unrestricted fields skip removeInvalidNodes and keep their hard breaks.
- Same line has a latent typo —
paragrah should be paragraph (the basic-node fallback for paragraphs is currently dead; it only works because paragraph is usually in the field's allowed list).
- The new Block Editor (
@dotcms/new-block-editor, behind FEATURE_FLAG_NEW_BLOCK_EDITOR, default false) does not filter content on load (uses setContent via normalizeEditorContent) and is not affected.
Steps to Reproduce
- Create a content type with a Block Editor field that is configured with allowed block restrictions (e.g. allow Heading + Paragraph + Lists — anything that makes
allowedBlocks.length > 1).
- Create/edit a contentlet, and in that field type a paragraph that uses Shift + Enter to insert line breaks (e.g. a multi-line address within a single paragraph).
- Save the contentlet. (Inspect the stored field JSON — the
hardBreak nodes are present and correct.)
- Re-open the same contentlet for editing.
- Observe: the line breaks are gone — the previously multi-line text now appears on a single line. Saving again persists the content without the line breaks.
Acceptance Criteria
dotCMS Version
Latest from main branch (legacy Block Editor, which is the default — FEATURE_FLAG_NEW_BLOCK_EDITOR defaults to false).
Severity
High - Major functionality broken
Links
Problem Statement
In the (legacy) Block Editor, hard breaks (line breaks created with Shift + Enter within a paragraph) are silently erased when content is re-opened for editing. The content is saved correctly to disk, but when the author opens the field again in the editor, every
hardBreakis gone and the text collapses onto a single line. If the author then saves without manually re-adding every line break, the content is persisted incorrectly.Example: an address laid out on multiple lines within one paragraph (using Shift+Enter) appears on a single line the next time the contentlet is edited.
Impact: Silent data loss / formatting loss on edit. Authors must remember to re-insert every line break before every save, which is error-prone. Reported by a customer.
Browser/OS: Not browser-specific — defect is in the editor's content-load logic.
Root cause (from code investigation)
core-web/libs/block-editor/src/lib/shared/utils/parser.utils.ts:35:When a Block Editor field has allowed-block restrictions (
allowedBlocks.length > 1),dot-block-editor.component.ts(setEditorJSONContent, ~line 707) runs the loaded JSON throughremoveInvalidNodes→purifyNodeTree, which drops any node whosetypeis not in the allowed map.hardBreakis never present inbasicNodesnor inrelatedContent, so everyhardBreaknode is stripped on load.Notes:
allowedBlocks.length > 1); unrestricted fields skipremoveInvalidNodesand keep their hard breaks.paragrahshould beparagraph(the basic-node fallback for paragraphs is currently dead; it only works becauseparagraphis usually in the field's allowed list).@dotcms/new-block-editor, behindFEATURE_FLAG_NEW_BLOCK_EDITOR, defaultfalse) does not filter content on load (usessetContentvianormalizeEditorContent) and is not affected.Steps to Reproduce
allowedBlocks.length > 1).hardBreaknodes are present and correct.)Acceptance Criteria
hardBreak/ Shift+Enter) are preserved when re-opening Block Editor content for editing, including on fields configured with allowed-block restrictions.hardBreakis treated as an always-allowed basic node sopurifyNodeTree/removeInvalidNodesnever strips it.paragrah→paragraphtypo inbasicNodesso the paragraph fallback works as intended.hardBreakinto a restricted-blocks editor and assert thehardBreaknodes surviveremoveInvalidNodes.dotCMS Version
Latest from
mainbranch (legacy Block Editor, which is the default —FEATURE_FLAG_NEW_BLOCK_EDITORdefaults tofalse).Severity
High - Major functionality broken
Links