Rich text editor silently loses all block formatting when its value is set before the component is rendered (Jmix 2.7.4)

Hey there!

We ran into a nasty one with richTextEditor in Jmix 2.7.4, and it took us a while to pin down because it only reproduces in some browsers.

Symptom. When a richTextEditor is placed inside a dialog and its HTML value is set as the dialog opens, all block formatting is silently lost — <h1>, <blockquote>, <ol>, <ul> all disappear and the content collapses into a single line. Inline formatting (bold, italic, underline, sub, sup) survives.

Root cause. setPresentationValue calls callJsFunction("setHtmlValueInternal", html), which calls this._editor.clipboard.convert(html). Quill 1.3.7 decides block-ness in isLine(node) using window.getComputedStyle(node).display, testing ['block','list-item'].indexOf(display) > -1. If that subtree is not being rendered yet, the browser returns a truncated computed-style declaration where display is "", so every element fails the block test. Since block structure in a Delta exists only as attributes on \n characters, no attributed newlines are emitted and every block format is dropped — while inline formats attach to text ops and survive.

We measured 475 style properties with display: "" during conversion, versus 669 with display: "block" once rendered.

Worth stressing: the component is attached at that moment (hostConnected: true, rootConnected: true). Attachment is not enough — it has to be rendered.

Reproduction. Minimal project attached: one view, one dialog, one richTextEditor, no database.

  • Click Open editor in dialog — the dialog sets <h1>Header</h1><blockquote>Quote</blockquote><ol><li>one</li><li>two</li></ol><p><strong>bold</strong></p> in its InitEvent
    • In Firefox the editor shows HeaderQuoteonetwobold as one flat paragraph:
<p>HeaderQuoteonetwo<strong>bold</strong></p>
  • Click Set the same HTML again (the editor is rendered by now), and it converts correctly:
<h1>Header</h1><blockquote>Quote</blockquote><ol><li>one</li><li>two</li></ol><p><strong>bold</strong></p>

Same HTML, same component, same session — only the render timing differs.

Browser dependence. Reproduces in Firefox 143.0.1. Does not reproduce in Chrome on macOS, which is fast enough to win the race. This made it look machine-specific for quite a while.

Why it matters. The damage is persistent. If a user on an affected browser opens a record and saves it, the flattened HTML is written back and the content is corrupted in the database for everyone.

Our workaround. Wait for a real layout signal (ResizeObserver, first callback with a non-zero contentRect) and re-apply the HTML via Quill’s public API, editor.setContents(editor.clipboard.convert(html), 'api'), sourcing the HTML from the server-side model value — the component’s own htmlValue is already flattened by then. Source 'api' rather than 'silent' matters, since only a non-silent source fires text-change and syncs the corrected value back to the server.

Would it make sense for setHtmlValueInternal to defer the conversion until the component is actually rendered?

Chromium correct dialog:


Firefox borken client state:

Firefox borken dialog:

Firefox fixed after resend:

Thanks in advance!

jmix-rte-repro-minimal.zip (61.4 KB)
minimal repro app

Hi,

Thank you for reporting the problem. It is a known one. We’ll consider looking into it again (moved the issue to triage), using the detailed information that you have provided.

Regards,
Gleb