Skip to content

fix: handle flushSync error when remounting useEditor in React 19#7549

Open
bxff wants to merge 3 commits intoueberdosis:mainfrom
bxff:fix/flushsync-react19-remount
Open

fix: handle flushSync error when remounting useEditor in React 19#7549
bxff wants to merge 3 commits intoueberdosis:mainfrom
bxff:fix/flushsync-react19-remount

Conversation

@bxff
Copy link

@bxff bxff commented Mar 1, 2026

Closes #7543

What this PR does

Wraps the flushSync call in ReactRenderer's constructor with a try-catch. When flushSync throws (because React is already in a render/commit phase), it falls back to queueMicrotask — the same path already used for non-initialized editors.

  • Normal case: flushSync succeeds → synchronous ProseMirror/React rendering (no behavior change)
  • Edge case (React 19 remount during commit): flushSync throws → graceful fallback to async render

Why

React 19 may remount keyed components when a list is reordered, even with stable keys. When this happens during useEditor's effect cycle, ReactRenderer gets constructed during React's commit phase, and flushSync throws a hard error.

Reproduction

Reporter's React 19 sandbox: https://codesandbox.io/p/sandbox/tiptap-flushsync-reorder-19-x47nq7

React 18 comparison (works fine): https://codesandbox.io/p/sandbox/tiptap-flushsync-reorder-18-w7ttcg

@changeset-bot
Copy link

changeset-bot bot commented Mar 1, 2026

🦋 Changeset detected

Latest commit: 7c1037b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 72 packages
Name Type
@tiptap/react Patch
@tiptap/extension-drag-handle-react Patch
@tiptap/core Patch
@tiptap/extension-audio Patch
@tiptap/extension-blockquote Patch
@tiptap/extension-bold Patch
@tiptap/extension-bubble-menu Patch
@tiptap/extension-bullet-list Patch
@tiptap/extension-code-block-lowlight Patch
@tiptap/extension-code-block Patch
@tiptap/extension-code Patch
@tiptap/extension-collaboration-caret Patch
@tiptap/extension-collaboration Patch
@tiptap/extension-color Patch
@tiptap/extension-details Patch
@tiptap/extension-document Patch
@tiptap/extension-drag-handle-vue-2 Patch
@tiptap/extension-drag-handle-vue-3 Patch
@tiptap/extension-drag-handle Patch
@tiptap/extension-emoji Patch
@tiptap/extension-file-handler Patch
@tiptap/extension-floating-menu Patch
@tiptap/extension-font-family Patch
@tiptap/extension-hard-break Patch
@tiptap/extension-heading Patch
@tiptap/extension-highlight Patch
@tiptap/extension-horizontal-rule Patch
@tiptap/extension-image Patch
@tiptap/extension-invisible-characters Patch
@tiptap/extension-italic Patch
@tiptap/extension-link Patch
@tiptap/extension-list Patch
@tiptap/extension-mathematics Patch
@tiptap/extension-mention Patch
@tiptap/extension-node-range Patch
@tiptap/extension-ordered-list Patch
@tiptap/extension-paragraph Patch
@tiptap/extension-strike Patch
@tiptap/extension-subscript Patch
@tiptap/extension-superscript Patch
@tiptap/extension-table-of-contents Patch
@tiptap/extension-table Patch
@tiptap/extension-text-align Patch
@tiptap/extension-text-style Patch
@tiptap/extension-text Patch
@tiptap/extension-twitch Patch
@tiptap/extension-typography Patch
@tiptap/extension-underline Patch
@tiptap/extension-unique-id Patch
@tiptap/extension-youtube Patch
@tiptap/extensions Patch
@tiptap/html Patch
@tiptap/markdown Patch
@tiptap/pm Patch
@tiptap/starter-kit Patch
@tiptap/static-renderer Patch
@tiptap/suggestion Patch
@tiptap/vue-2 Patch
@tiptap/vue-3 Patch
@tiptap/extension-character-count Patch
@tiptap/extension-dropcursor Patch
@tiptap/extension-focus Patch
@tiptap/extension-gapcursor Patch
@tiptap/extension-history Patch
@tiptap/extension-list-item Patch
@tiptap/extension-list-keymap Patch
@tiptap/extension-placeholder Patch
@tiptap/extension-table-cell Patch
@tiptap/extension-table-header Patch
@tiptap/extension-table-row Patch
@tiptap/extension-task-item Patch
@tiptap/extension-task-list Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify
Copy link

netlify bot commented Mar 1, 2026

Deploy Preview for tiptap-embed ready!

Name Link
🔨 Latest commit 7c1037b
🔍 Latest deploy log https://app.netlify.com/projects/tiptap-embed/deploys/69aafd2fc184fa000837ae07
😎 Deploy Preview https://deploy-preview-7549--tiptap-embed.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@bxff bxff force-pushed the fix/flushsync-react19-remount branch from 9f9716b to 5eda073 Compare March 1, 2026 00:46
@bxff bxff changed the base branch from main to develop March 1, 2026 00:47
Wraps the flushSync call in ReactRenderer's constructor with a try-catch.
When flushSync throws (because React is already in a render/commit phase),
it falls back to queueMicrotask — the same path already used for
non-initialized editors.

Closes ueberdosis#7543
@bxff bxff force-pushed the fix/flushsync-react19-remount branch from 5eda073 to 0f34655 Compare March 1, 2026 00:48
@bdbch bdbch changed the base branch from develop to main March 14, 2026 15:04
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.

[Bug]: flushSync error when remounting useEditor with existing ID and ReactNodeViewRenderer in React 19

1 participant