Skip to content

fix: set width attribute on image and video elements in editor render#2740

Merged
nperez0111 merged 4 commits into
mainfrom
feat/readonly-image-width
May 14, 2026
Merged

fix: set width attribute on image and video elements in editor render#2740
nperez0111 merged 4 commits into
mainfrom
feat/readonly-image-width

Conversation

@nperez0111
Copy link
Copy Markdown
Contributor

@nperez0111 nperez0111 commented May 13, 2026

Summary

When the editor is rendered with editable: false, the <img> and <video> tags did not include a width attribute even though the block's previewWidth prop was set.

Fixes #2726

Rationale

The width was only applied as an inline style on the wrapper <div>, not on the media element itself. This prevented the browser from reserving space before the media loaded (CLS), and made the rendered DOM lossy for downstream consumers reading innerHTML from a read-only editor.

While auditing, I found the same issue in the video block — plus the React VideoToExternalHTML was dropping previewWidth on export entirely. Audio and File blocks don't have a width concept, so they're unaffected.

Changes

  • packages/react/src/blocks/Image/block.tsx: set width on ImagePreview's <img>.
  • packages/core/src/blocks/Image/block.ts: set image.width (when defined) in imageRender.
  • packages/react/src/blocks/Video/block.tsx: set width on VideoPreview's <video> and also on VideoToExternalHTML (was missing).
  • packages/core/src/blocks/Video/block.ts: guard the existing video.width = previewWidth against undefined (previously coerced to 0).
  • Updated ServerBlockNoteEditor snapshot to reflect the new width="256" attribute.

Impact

CSS width: 100% on .bn-visual-media still controls layout, so resize behavior in editable mode is unaffected — the HTML attribute is purely additive and advertises the intrinsic width to read-only consumers.

Testing

  • pnpm --filter @blocknote/server-util test passes (snapshot updated).
  • pnpm --filter @blocknote/react test passes.
  • pnpm --filter @blocknote/core test passes (an unrelated numbered-list performance test is flaky on this machine but is not touched by this change).

Summary by CodeRabbit

  • Bug Fixes
    • Image and Video block previews now consistently honor the preview width setting across all preview and external rendering paths, resulting in uniform sizing and improved visual consistency between editor previews and exported/embedded outputs.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blocknote Ready Ready Preview May 14, 2026 6:46am
blocknote-website Ready Ready Preview May 14, 2026 6:46am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 944b14d9-34e4-424a-8ac2-3edfa4fc058f

📥 Commits

Reviewing files that changed from the base of the PR and between b1cf009 and 32f246c.

⛔ Files ignored due to path filters (11)
  • packages/server-util/src/context/__snapshots__/ServerBlockNoteEditor.test.ts.snap is excluded by !**/*.snap, !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/nested.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noCaption.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noName.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noCaption.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noName.html is excluded by !**/__snapshots__/**
📒 Files selected for processing (4)
  • packages/core/src/blocks/Image/block.ts
  • packages/core/src/blocks/Video/block.ts
  • packages/react/src/blocks/Image/block.tsx
  • packages/react/src/blocks/Video/block.tsx
✅ Files skipped from review due to trivial changes (2)
  • packages/react/src/blocks/Image/block.tsx
  • packages/core/src/blocks/Video/block.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react/src/blocks/Video/block.tsx
  • packages/core/src/blocks/Image/block.ts

📝 Walkthrough

Walkthrough

This PR applies previewWidth as an HTML width attribute in core and React preview/export renderers for Image and Video, so preview elements include the block's width when present.

Changes

Media preview width propagation

Layer / File(s) Summary
Apply previewWidth to image renders
packages/core/src/blocks/Image/block.ts, packages/react/src/blocks/Image/block.tsx
imageRender conditionally sets width from block.props.previewWidth on the HTML <img> element; ImagePreview sets width={props.block.props.previewWidth} on the preview <img> element.
Apply previewWidth to video renders
packages/core/src/blocks/Video/block.ts, packages/react/src/blocks/Video/block.tsx
Core video render now sets video.width only when block.props.previewWidth is provided; VideoPreview and VideoToExternalHTML include `width={props.block.props.previewWidth

Sequence Diagram(s)

sequenceDiagram
  participant CoreImageRender
  participant ImagePreview
  participant IMGElement
  CoreImageRender->>IMGElement: set width = block.props.previewWidth (if present)
  ImagePreview->>IMGElement: render <img width={props.block.props.previewWidth}>
Loading
sequenceDiagram
  participant CoreVideoRender
  participant VideoPreview
  participant VideoToExternalHTML
  participant VIDEOElement
  CoreVideoRender->>VIDEOElement: set width = block.props.previewWidth (if truthy)
  VideoPreview->>VIDEOElement: render <video width={props.block.props.previewWidth || undefined}>
  VideoToExternalHTML->>VIDEOElement: emit preview-mode <video width={props.block.props.previewWidth || undefined}>
Loading

🎯 3 (Moderate) | ⏱️ ~20 minutes

"I munch on bytes beneath the moonlit code,
Widths tucked in tags so pages hold their road,
No sudden jumps as pixels find their place,
Calm scrolls and steady views — a quiet interface. 🐰"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: setting width attributes on image and video elements in editor render mode.
Description check ✅ Passed The description covers all major template sections with clear explanations of the problem, rationale, changes, impact, and testing results.
Linked Issues check ✅ Passed The PR addresses issue #2726 by emitting width attributes on image and video elements during HTML serialization, enabling browsers to reserve space and prevent CLS.
Out of Scope Changes check ✅ Passed All changes are directly related to adding width attributes to image and video elements, with snapshot updates as necessary side effects. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/readonly-image-width

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 13, 2026

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/@blocknote/ariakit@2740

@blocknote/code-block

npm i https://pkg.pr.new/@blocknote/code-block@2740

@blocknote/core

npm i https://pkg.pr.new/@blocknote/core@2740

@blocknote/mantine

npm i https://pkg.pr.new/@blocknote/mantine@2740

@blocknote/react

npm i https://pkg.pr.new/@blocknote/react@2740

@blocknote/server-util

npm i https://pkg.pr.new/@blocknote/server-util@2740

@blocknote/shadcn

npm i https://pkg.pr.new/@blocknote/shadcn@2740

@blocknote/xl-ai

npm i https://pkg.pr.new/@blocknote/xl-ai@2740

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/@blocknote/xl-docx-exporter@2740

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/@blocknote/xl-email-exporter@2740

@blocknote/xl-multi-column

npm i https://pkg.pr.new/@blocknote/xl-multi-column@2740

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/@blocknote/xl-odt-exporter@2740

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/@blocknote/xl-pdf-exporter@2740

commit: 32f246c

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react/src/blocks/Video/block.tsx`:
- Line 30: The React implementation passes props.block.props.previewWidth
directly into the width attribute causing width="0" to be emitted; update
VideoPreview and VideoToExternalHTML so they only set the width attribute when
props.block.props.previewWidth is truthy (i.e., use a conditional guard like the
core block does) — locate the uses of props.block.props.previewWidth in the
VideoPreview and VideoToExternalHTML components and change them to only assign
width when that value is truthy.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 418a9712-4fa6-4ba6-a89f-067ec85746cb

📥 Commits

Reviewing files that changed from the base of the PR and between b2aa79c and b1cf009.

⛔ Files ignored due to path filters (10)
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/nested.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noCaption.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noName.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video.html is excluded by !**/__snapshots__/**
  • tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noCaption.html is excluded by !**/__snapshots__/**
  • tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noName.html is excluded by !**/__snapshots__/**
📒 Files selected for processing (2)
  • packages/core/src/blocks/Video/block.ts
  • packages/react/src/blocks/Video/block.tsx

Comment thread packages/react/src/blocks/Video/block.tsx Outdated
When the editor is rendered with editable: false, the <img> tag did not
include a width attribute even though the block's previewWidth prop was
set — the width was only applied as inline style on the wrapper. This
prevented the browser from reserving space before the image loaded
(CLS) and made the rendered DOM lossy for downstream consumers reading
innerHTML.

Apply previewWidth directly to the <img> element in the editor render
path, mirroring what imageToExternalHTML and the core Video block
already do. CSS width: 100% on .bn-visual-media still controls layout
so resize behavior is unaffected.

Fixes #2726
Apply the same fix as the image block to video:
- React VideoPreview now sets width on the <video> element so read-only
  rendering includes it.
- React VideoToExternalHTML now serializes previewWidth (it was
  previously dropped entirely on export).
- Core video render path guards `video.width = previewWidth` against
  undefined (assigning undefined to a numeric DOM property coerces to
  0).

Audio and File blocks don't have a previewWidth prop, so no change is
needed there.

Refs #2726
- Image snapshots now include the new width attribute on the <img>
  element when previewWidth is set.
- Video snapshots no longer include width="0" since the core video
  render now guards against an undefined previewWidth.

Refs #2726
@nperez0111 nperez0111 force-pushed the feat/readonly-image-width branch from ba0d061 to 32f246c Compare May 14, 2026 06:41
@nperez0111 nperez0111 merged commit 563e5bc into main May 14, 2026
23 checks passed
@nperez0111 nperez0111 deleted the feat/readonly-image-width branch May 14, 2026 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant