Conversation
…pense reveal An exit that mounts together with entries rendered after it (as in `ListItemView`) shows its entries only once its observer subscribes, in a passive effect. React runs the passive effects of a Suspense reveal or a transition after paint, so the browser painted one frame with an empty exit. The exit now re-renders from a layout effect until it has rendered committed entries; that update runs before paint. The "previously suspended tunnel entry" test advanced the fake timers once and relied on React pre-rendering the suspended siblings before that. The extra sync render moves the pre-render to a later task, so the test now advances until the entries are there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Coverage Report for ./packages/components/
File CoverageNo changed files found. |
Contributor
🚀 Preview DeploymentPreview environments are ready:
Images:
|
mfal
marked this pull request as ready for review
September 28, 2026 15:51
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
A
TunnelExitthat mounts in a Suspense reveal stays empty for one painted frame. In mStudio this makes every row of aListwhose items load individually jump: aListItemViewrenders for one frame without its tunnelledAvatar,HeadingandText(36 px instead of 61 px), then snaps back.Cause:
ListItemViewrenders its exits before the tunnelled children, so the exit's first render finds no entries.observer, and it subscribes viauseSyncExternalStorein a passive effect.Fix: until the exit has rendered committed entries, it dispatches a re-render from a layout effect. React processes that update after every layout effect of the commit and before paint. Cost: one extra render per exit mount while it has no committed entries.
Tests
MutationObservercallback after the reveal commit. It is red onmain('') and green with the fix.Lazy500's timer) before the advance. The extra sync render moves the pre-render to a later task, so the test now advances invitest.waitForuntil the entries are there. The assertions are unchanged.List,Label,HeadingandTabsbrowser tests in components pass (59 tests).node_modules: 0 empty rows in 2 runs, down from all 20 rows for at least one painted frame.Checklist
pnpm lintis clean (pre-push hook);pnpm affected:testnot run in full — see Tests🤖 Generated with Claude Code