Skip to content

Don't outline Suspense boundaries with suspensey CSS during shell flush#545

Closed
everettbu wants to merge 1 commit into
mainfrom
disable-css-outlining-in-shell
Closed

Don't outline Suspense boundaries with suspensey CSS during shell flush#545
everettbu wants to merge 1 commit into
mainfrom
disable-css-outlining-in-shell

Conversation

@everettbu

@everettbu everettbu commented Feb 19, 2026

Copy link
Copy Markdown

Mirror of facebook/react#35824
Original author: gnoff


When flushing the shell, stylesheets with precedence are emitted in the <head> which blocks paint regardless. Outlining a boundary solely because it has suspensey CSS provides no benefit during the shell flush and causes a higher-level fallback to be shown unnecessarily (e.g. "Middle Fallback" instead of "Inner Fallback").

This change passes a flushingInShell flag to hasSuspenseyContent so the host config can skip stylesheet-only suspensey content when flushing the shell. Suspensey images (used for ViewTransition animation reveals) still trigger outlining during the shell since their motivation is different.

When flushing streamed completions the behavior is unchanged — suspensey CSS still causes outlining so the parent content can display sooner while the stylesheet loads.

When flushing the shell, stylesheets with precedence are emitted in the <head> which blocks paint regardless. Outlining a boundary solely because it has suspensey CSS provides no benefit during the shell flush and causes a higher-level fallback to be shown unnecessarily (e.g. "Middle Fallback" instead of "Inner Fallback").

This change passes a flushingInShell flag to hasSuspenseyContent so the host config can skip stylesheet-only suspensey content when flushing the shell. Suspensey images (used for ViewTransition animation reveals) still trigger outlining during the shell since their motivation is different.

When flushing streamed completions the behavior is unchanged — suspensey CSS still causes outlining so the parent content can display sooner while the stylesheet loads.
@everettbu everettbu added CLA Signed React Core Team Opened by a member of the React Core Team labels Feb 19, 2026
@greptile-apps

greptile-apps Bot commented Feb 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR prevents unnecessary outlining of Suspense boundaries that only contain suspensey CSS (stylesheets with precedence) during the shell flush. Since those stylesheets are already emitted in the <head> and block paint regardless, outlining them provides no benefit and causes a higher-level fallback to display instead of the innermost one.

  • Adds a flushingInShell parameter to hasSuspenseyContent across all host configs (DOM, DOM Legacy, Markup, Noop)
  • Introduces a module-level flushingShell flag in ReactFizzServer.js that is set during root segment flushing
  • During shell flush, only suspensey images (used for ViewTransition animation reveals) still trigger outlining; CSS-only boundaries are inlined
  • Streamed completion behavior is unchanged — suspensey CSS still causes outlining during streaming
  • Includes three new test cases covering shell flush CSS skipping, streamed completion CSS outlining, and ViewTransition image outlining during shell flush

Confidence Score: 4/5

  • This PR is safe to merge — it's a targeted optimization with correct logic and thorough test coverage.
  • The change is well-scoped and logically sound: skipping CSS-only outlining during shell flush is a valid optimization since stylesheets in the <head> already block paint. All host config implementations are consistently updated, the isEligibleForOutlining call correctly always passes false, and three new tests cover the key scenarios. One minor style concern: the flushingShell flag lacks a defensive finally reset unlike the analogous flushingPartialBoundaries flag.
  • packages/react-server/src/ReactFizzServer.js — the new flushingShell module-level flag lacks a finally reset unlike its sibling flushingPartialBoundaries.

Important Files Changed

Filename Overview
packages/react-server/src/ReactFizzServer.js Introduces flushingShell module-level flag to pass shell-flush context through to hasSuspenseyContent. The flag is set/reset around the root segment flush. Minor concern: unlike flushingPartialBoundaries, this flag is not defensively reset in the finally block.
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js Core logic change: hasSuspenseyContent now accepts flushingInShell and skips stylesheet-based outlining during shell flush while preserving suspensey image outlining. Logic is correct and well-commented.
packages/react-dom/src/tests/ReactDOMFloat-test.js Adds three well-structured tests covering: shell flush skips CSS outlining, streamed completion still outlines for CSS, and ViewTransition images still outline during shell flush. Good coverage of the behavioral change.
packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js Signature update only — adds flushingInShell parameter but behavior unchanged (always returns false).
packages/react-markup/src/ReactFizzConfigMarkup.js Signature update only — adds flushingInShell parameter but behavior unchanged (always returns false).
packages/react-noop-renderer/src/ReactNoopServer.js Signature update only — adds flushingInShell parameter but behavior unchanged (always returns false).

Last reviewed commit: adc3be4

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +5889 to +5891
flushingShell = true;
flushSegment(request, destination, completedRootSegment, null);
flushingShell = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing finally reset for flushingShell

The sibling module-level flag flushingPartialBoundaries is defensively reset in the finally block (line 5978) to avoid it getting stuck as true if an exception is thrown. The new flushingShell flag follows a similar set/call/reset pattern but lacks the same finally safety net.

If flushSegment were to throw here, flushingShell would remain true for subsequent calls. While this is unlikely to cause real issues in practice (the root segment is flushed once and a throw here likely leaves the stream in a fatal state), adding flushingShell = false to the finally block would be consistent with the existing defensive pattern for flushingPartialBoundaries.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-server/src/ReactFizzServer.js
Line: 5889-5891

Comment:
**Missing `finally` reset for `flushingShell`**

The sibling module-level flag `flushingPartialBoundaries` is defensively reset in the `finally` block (line 5978) to avoid it getting stuck as `true` if an exception is thrown. The new `flushingShell` flag follows a similar set/call/reset pattern but lacks the same `finally` safety net.

If `flushSegment` were to throw here, `flushingShell` would remain `true` for subsequent calls. While this is unlikely to cause real issues in practice (the root segment is flushed once and a throw here likely leaves the stream in a fatal state), adding `flushingShell = false` to the `finally` block would be consistent with the existing defensive pattern for `flushingPartialBoundaries`.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@everettbu

Copy link
Copy Markdown
Author

Upstream PR was closed or merged. Code is synced via branch mirror.

@everettbu everettbu closed this Feb 19, 2026
@everettbu
everettbu deleted the disable-css-outlining-in-shell branch February 19, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants