Skip to content

fix: React custom blocks cause infinite rerender on mobile (BLO-1212)#2830

Open
matthewlipski wants to merge 2 commits into
mainfrom
mobile-react-infinite-rerender
Open

fix: React custom blocks cause infinite rerender on mobile (BLO-1212)#2830
matthewlipski wants to merge 2 commits into
mainfrom
mobile-react-infinite-rerender

Conversation

@matthewlipski

@matthewlipski matthewlipski commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes an issue with React custom blocks sometimes causing infinite re-render loops on mobile. This can be seen most easily with the Alert Block with Full UX example, where attempting to insert an alert block via the slash menu will result in an infinite re-render loop and crash. This can be replicated via the mobile emulator in Chrome dev tools.

The reason this happens is that the way ProseMirror detects when a block's editable content has changed is different on desktop vs mobile. On desktop, it's detected using key events within the editable content wrapper element. On mobile though, key inputs don't use actually use typical key events, but insert characters using IME composition (see here and here). Therefore, ProseMirror instead detects changes in the editable content based on DOM mutations. Yet for some reason, re-renders are triggered by DOM mutations anywhere in the block, not just within its editable content.

And so the problem is that React's own rendering also causes DOM mutations, leading to ProseMirror triggering a re-render, causing React to render the block again, and starting an infinite loop. Therefore, we need to explicitly tell ProseMirror to ignore mutations outside the editable content to avoid this from happening.

Closes #2804

Rationale

This is a severe issue on mobile.

Changes

  • Added ignoreMutation to createReactBlockSpec to ignore DOM mutations outside of block's editable content.

Impact

N/A

Testing

N/A (we don't have testing infrastructure on mobile)

Screenshots/Video

N/A

Checklist

  • Code follows the project's coding standards.
  • Unit tests covering the new feature have been added.
  • All existing tests pass.
  • The documentation has been updated to reflect the new feature

Additional Notes

N/A

Summary by CodeRabbit

  • Performance
    • Reduced unnecessary editor re-renders triggered by React-driven DOM changes outside editable content areas, improving responsiveness and smoothing block editing. Selection changes remain unaffected.
  • Chores
    • No changes to public APIs or exported types; internal behavior adjusted only.

@vercel

vercel Bot commented Jun 4, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
blocknote Error Error Jun 8, 2026 5:36pm
blocknote-website Error Error Jun 8, 2026 5:36pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: cf283d54-5150-4b54-9b50-0babfa6abcae

📥 Commits

Reviewing files that changed from the base of the PR and between 57951dc and 1daebc3.

📒 Files selected for processing (1)
  • packages/react/src/schema/ReactBlockSpec.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react/src/schema/ReactBlockSpec.tsx

📝 Walkthrough

Walkthrough

This PR adds an ignoreMutation handler to the React node-view rendering options in ReactBlockSpec.tsx. The handler filters out selection mutations and mutations targeting the block's [data-node-view-content] wrapper element, allowing only external mutations to trigger ProseMirror updates.

Changes

React node-view mutation filtering

Layer / File(s) Summary
ignoreMutation handler in ReactNodeViewRenderer
packages/react/src/schema/ReactBlockSpec.tsx
Adds an ignoreMutation callback that early-returns for selection mutations, locates the closest [data-node-view-content] wrapper for the mutation target, ignores mutations when no wrapper is found, and ignores attributes mutations on the wrapper itself; other mutations are allowed to proceed.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested Reviewers

  • YousefED
  • nperez0111

Poem

A rabbit peers where mutations play,
Ignoring noise that gets in the way,
Wrapping content with careful grace,
Letting only true updates take place. 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: React custom blocks cause infinite rerender on mobile (BLO-1212)' clearly and accurately summarizes the main change in the PR.
Description check ✅ Passed The description covers all major template sections with substantial detail: summary, rationale, changes, impact, testing, and a completed checklist explaining what was and wasn't done.
Linked Issues check ✅ Passed The PR successfully addresses issue #2804 by implementing the ignoreMutation handler to prevent React-triggered DOM mutations from causing infinite re-render loops on mobile.
Out of Scope Changes check ✅ Passed All changes are scoped to the stated objective: adding ignoreMutation logic to ReactBlockSpec to suppress re-renders from DOM mutations outside editable content.

✏️ 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 mobile-react-infinite-rerender

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

pkg-pr-new Bot commented Jun 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

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

@blocknote/code-block

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

@blocknote/core

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

@blocknote/mantine

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

@blocknote/react

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

@blocknote/server-util

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

@blocknote/shadcn

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

@blocknote/xl-ai

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

@blocknote/xl-docx-exporter

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

@blocknote/xl-email-exporter

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

@blocknote/xl-multi-column

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

@blocknote/xl-odt-exporter

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

@blocknote/xl-pdf-exporter

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

commit: 57951dc

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://TypeCellOS.github.io/BlockNote/pr-preview/pr-2830/

Built to branch gh-pages at 2026-06-04 08:49 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/react/src/schema/ReactBlockSpec.tsx (1)

318-350: 🏗️ Heavy lift

Add a mobile regression test for mutation-type filtering.

Lines [318-350] implement behavior that is sensitive to mutation type and target; without regression coverage, this is easy to break. Add a test that asserts wrapper attributes mutations are ignored, while content childList/characterData mutations are not ignored.

🤖 Prompt for 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.

In `@packages/react/src/schema/ReactBlockSpec.tsx` around lines 318 - 350, Add a
regression test for the ReactBlockSpec.ignoreMutation behavior: create a test
that constructs a node-view using the ReactBlockSpec (or the same block spec
used in tests) and simulates mutations against the wrapper element and the inner
editable content; assert that an attributes mutation on the editable wrapper
returns true (ignored), while childList and characterData mutations on the
content return false (not ignored). Target the ignoreMutation function in
ReactBlockSpec.tsx and exercise cases for mutation.type === "selection" (should
return false), an attributes mutation with mutation.target === content wrapper
(true), and content childList/characterData mutations where mutation.target is
inside [data-node-view-content] (false).
🤖 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/schema/ReactBlockSpec.tsx`:
- Around line 343-347: In ReactBlockSpec.tsx inside the ignoreMutation
implementation (the ignoreMutation function used by the ReactNodeView), stop
returning true for all mutation types when mutation.target === content; instead
only ignore wrapper attribute changes by checking that mutation.type ===
"attributes" && mutation.target === content before returning true so
childList/textInput mutations on the [data-node-view-content] element are not
suppressed.

---

Nitpick comments:
In `@packages/react/src/schema/ReactBlockSpec.tsx`:
- Around line 318-350: Add a regression test for the
ReactBlockSpec.ignoreMutation behavior: create a test that constructs a
node-view using the ReactBlockSpec (or the same block spec used in tests) and
simulates mutations against the wrapper element and the inner editable content;
assert that an attributes mutation on the editable wrapper returns true
(ignored), while childList and characterData mutations on the content return
false (not ignored). Target the ignoreMutation function in ReactBlockSpec.tsx
and exercise cases for mutation.type === "selection" (should return false), an
attributes mutation with mutation.target === content wrapper (true), and content
childList/characterData mutations where mutation.target is inside
[data-node-view-content] (false).
🪄 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: 715e9430-fa98-4470-b143-703543153e9f

📥 Commits

Reviewing files that changed from the base of the PR and between ec9c151 and 57951dc.

📒 Files selected for processing (1)
  • packages/react/src/schema/ReactBlockSpec.tsx

Comment thread packages/react/src/schema/ReactBlockSpec.tsx Outdated

@nperez0111 nperez0111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's double check with Tiptap's new React Renderer, just to make sure we aren't fixing something that is fixed.

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.

Alert Block freezes editor when selected from Slash Menu on mobile

2 participants