Skip to content

Image block's width is not serialized to HTML attributes, causing layout shift (CLS) when rendering exported HTML #2726

@yannishin

Description

@yannishin

What’s broken?

The image block stores a width prop (used at edit time for resize handles), but this width is not emitted as an HTML width attribute when serializing to HTML via blocksToHTMLLossy / blocksToFullHTML. The exported <img> tag has no dimensions — currently the output looks like <img src="..." alt="..." />.

When this HTML is rendered in a non-editor context (read-only view, message bubble, document viewer, etc.), the browser cannot reserve space for the image before it loads. Once the image finishes loading, the layout shifts and surrounding content — including the user's scroll position — jumps. This is a Cumulative Layout Shift (CLS).

The width is already known to the editor and stored on the block, so it's being dropped on the export path. This also breaks the round-trip goal stated in #1583 (blocksToHTMLLossy output should recreate the original block 1:1) — without width in the HTML, parsing the output back into blocks loses the dimension.

What did you expect to happen?

The serialized <img> tag should include the block's known dimensions as native HTML attributes — e.g. <img src="..." alt="..." width="640" height="480" />.

This lets the browser reserve space before the image loads (no CLS), preserves block data round-tripping (#1583), and matches the behavior of other rich text editors such as CKEditor.

If height isn't currently stored on the block, emitting width alone — or width + a CSS aspect-ratio once aspect ratio can be derived — would already meaningfully reduce shift.

Steps to reproduce

  1. Open https://www.blocknotejs.org/demo
  2. Insert an image block and resize it using the resize handle
  3. Run await editor.blocksToHTMLLossy(editor.document) (or blocksToFullHTML) and inspect the output
  4. Observe that the resulting <img> tag has no width attribute, even though the block's width prop is set

To observe the user-visible impact:

  1. Render that exported HTML in any browser with network throttling enabled
  2. Observe the image rendering at a small default size first, then jumping to its real size on load completion — surrounding content shifts and scroll position moves

BlockNote version

No response

Environment

No response

Additional context

Related issues:

In our deployment, end users observe the layout shift as the page "jumping" when scrolling through content with images that haven't loaded yet. This is especially disruptive in scroll-heavy contexts like message timelines and long documents. The issue did not occur when rendering content authored in CKEditor, where image dimensions were preserved on the wrapper element.

Screenshot attached showing the shift effect in a rendered message view.

Contribution

  • I'd be interested in contributing a fix for this issue

Sponsor

  • I'm a sponsor and would appreciate if you could look into this sooner than later 💖

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssue has not yet been reviewed or classified by maintainers.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions