Skip to content

Add onBrowserBailout Fizz option - #37193

Merged
gnoff merged 1 commit into
react:mainfrom
gnoff:jstory/on-browser-bailout
Aug 8, 2026
Merged

Add onBrowserBailout Fizz option#37193
gnoff merged 1 commit into
react:mainfrom
gnoff:jstory/on-browser-bailout

Conversation

@gnoff

@gnoff gnoff commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Adds a new Fizz option, onBrowserBailout, for observing intentional server-render bailouts caused by ReactDOM.browser() and future APIs that use the same recoverable error mechanism. The callback receives the original recoverable error and ErrorInfo, defaults to a noop, and runs only when Fizz successfully recovers by deferring work to the browser.

Recoverables consumed within Suspense or used to abort recoverable boundaries are reported through onBrowserBailout without also invoking onError. A bailout outside Suspense remains fatal and reports only through onError, with the original recoverable preserved as its cause, while directly throwing the value continues to behave like a normal render error.

Plumbs the option through the streaming, resume, and prerender entry points for Node, browser, Edge, Bun, FB, markup, and noop renderers while preserving the positional Fizz request API for callers that do not expose the option.

Uses an environment-neutral browser-only rendering message for the isomorphic browser() value and updates the production error mapping. Tests cover successful browser bailouts, recoverable abort reasons, root-fatal behavior, component stack information, the default noop behavior, and direct throws in development and production.

@meta-cla meta-cla Bot added the CLA Signed label Aug 3, 2026
@gnoff
gnoff force-pushed the jstory/on-browser-bailout branch from 1a48739 to a4eba06 Compare August 3, 2026 20:15
Adds a new Fizz option, `onBrowserBailout`, for observing intentional server-render bailouts caused by `ReactDOM.browser()` and future APIs that use the same recoverable error mechanism. The callback receives the original recoverable error and `ErrorInfo`, defaults to a noop, and runs only when Fizz successfully recovers by deferring work to the browser.

Recoverables consumed within Suspense or used to abort recoverable boundaries are reported through `onBrowserBailout` without also invoking `onError`. A bailout outside Suspense remains fatal and reports only through `onError`, with the original recoverable preserved as its cause, while directly throwing the value continues to behave like a normal render error.

Plumbs the option through the streaming, resume, and prerender entry points for Node, browser, Edge, Bun, FB, markup, and noop renderers while preserving the positional Fizz request API for callers that do not expose the option.

Uses an environment-neutral browser-only rendering message for the isomorphic `browser()` value and updates the production error mapping. Tests cover successful browser bailouts, recoverable abort reasons, root-fatal behavior, component stack information, the default noop behavior, and direct throws in development and production.
@gnoff
gnoff force-pushed the jstory/on-browser-bailout branch from a4eba06 to dde3dad Compare August 3, 2026 20:30

@eps1lon eps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

and runs only when Fizz successfully recovers by deferring work to the browser.

Are we testing this at the moment? Sounds like onBrowserBailout shouldn't run if a sibling in the same Suspense boundary throws.

@eps1lon eps1lon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Are we calling onBrowserBailout for each unique browser() invocation that causes a boundary to be deferred or each use callsite?

@github-actions github-actions Bot added the React Core Team Opened by a member of the React Core Team label Aug 4, 2026
@gnoff

gnoff commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Are we testing this at the moment? Sounds like onBrowserBailout shouldn't run if a sibling in the same Suspense boundary throws.

When you error a boundary in Fizz it's first one wins, we don't really consider a second error to be meaningful so if use(browser()) is the first error within a boundary it'll get reported and you won't observe any pending work that errors too. We could test but I don't know if it is meaningful since in Fizz browser() is really just an error with slightly different reporting mechanics and we could potentially change this in the future. (i.e. I think it can be unspecified behavior vs a public contract)

Are we calling onBrowserBailout for each unique browser() invocation that causes a boundary to be deferred or each use callsite?

We call it once per Boundary and it's first use wins (of course other errors win "more" if they are first per above)

@gnoff
gnoff force-pushed the jstory/on-browser-bailout branch from 2bf4be1 to dde3dad Compare August 6, 2026 22:42
@react-sizebot

Copy link
Copy Markdown

The size diff is too large to display in a single comment. The GitHub action for this pull request contains an artifact called 'sizebot-message.md' with the full message.

Generated by 🚫 dangerJS against dde3dad

@gnoff
gnoff merged commit 2042572 into react:main Aug 8, 2026
238 of 291 checks passed
@gnoff
gnoff deleted the jstory/on-browser-bailout branch August 8, 2026 02:31
github-actions Bot pushed a commit that referenced this pull request Aug 8, 2026
Adds a new Fizz option, `onBrowserBailout`, for observing intentional
server-render bailouts caused by `ReactDOM.browser()` and future APIs
that use the same recoverable error mechanism. The callback receives the
original recoverable error and `ErrorInfo`, defaults to a noop, and runs
only when Fizz successfully recovers by deferring work to the browser.

Recoverables consumed within Suspense or used to abort recoverable
boundaries are reported through `onBrowserBailout` without also invoking
`onError`. A bailout outside Suspense remains fatal and reports only
through `onError`, with the original recoverable preserved as its cause,
while directly throwing the value continues to behave like a normal
render error.

Plumbs the option through the streaming, resume, and prerender entry
points for Node, browser, Edge, Bun, FB, markup, and noop renderers
while preserving the positional Fizz request API for callers that do not
expose the option.

Uses an environment-neutral browser-only rendering message for the
isomorphic `browser()` value and updates the production error mapping.
Tests cover successful browser bailouts, recoverable abort reasons,
root-fatal behavior, component stack information, the default noop
behavior, and direct throws in development and production.

DiffTrain build for [2042572](2042572)
github-actions Bot pushed a commit that referenced this pull request Aug 8, 2026
Adds a new Fizz option, `onBrowserBailout`, for observing intentional
server-render bailouts caused by `ReactDOM.browser()` and future APIs
that use the same recoverable error mechanism. The callback receives the
original recoverable error and `ErrorInfo`, defaults to a noop, and runs
only when Fizz successfully recovers by deferring work to the browser.

Recoverables consumed within Suspense or used to abort recoverable
boundaries are reported through `onBrowserBailout` without also invoking
`onError`. A bailout outside Suspense remains fatal and reports only
through `onError`, with the original recoverable preserved as its cause,
while directly throwing the value continues to behave like a normal
render error.

Plumbs the option through the streaming, resume, and prerender entry
points for Node, browser, Edge, Bun, FB, markup, and noop renderers
while preserving the positional Fizz request API for callers that do not
expose the option.

Uses an environment-neutral browser-only rendering message for the
isomorphic `browser()` value and updates the production error mapping.
Tests cover successful browser bailouts, recoverable abort reasons,
root-fatal behavior, component stack information, the default noop
behavior, and direct throws in development and production.

DiffTrain build for [2042572](2042572)
github-actions Bot pushed a commit to code/lib-react that referenced this pull request Aug 8, 2026
Adds a new Fizz option, `onBrowserBailout`, for observing intentional
server-render bailouts caused by `ReactDOM.browser()` and future APIs
that use the same recoverable error mechanism. The callback receives the
original recoverable error and `ErrorInfo`, defaults to a noop, and runs
only when Fizz successfully recovers by deferring work to the browser.

Recoverables consumed within Suspense or used to abort recoverable
boundaries are reported through `onBrowserBailout` without also invoking
`onError`. A bailout outside Suspense remains fatal and reports only
through `onError`, with the original recoverable preserved as its cause,
while directly throwing the value continues to behave like a normal
render error.

Plumbs the option through the streaming, resume, and prerender entry
points for Node, browser, Edge, Bun, FB, markup, and noop renderers
while preserving the positional Fizz request API for callers that do not
expose the option.

Uses an environment-neutral browser-only rendering message for the
isomorphic `browser()` value and updates the production error mapping.
Tests cover successful browser bailouts, recoverable abort reasons,
root-fatal behavior, component stack information, the default noop
behavior, and direct throws in development and production.

DiffTrain build for [2042572](react@2042572)
github-actions Bot pushed a commit to code/lib-react that referenced this pull request Aug 8, 2026
Adds a new Fizz option, `onBrowserBailout`, for observing intentional
server-render bailouts caused by `ReactDOM.browser()` and future APIs
that use the same recoverable error mechanism. The callback receives the
original recoverable error and `ErrorInfo`, defaults to a noop, and runs
only when Fizz successfully recovers by deferring work to the browser.

Recoverables consumed within Suspense or used to abort recoverable
boundaries are reported through `onBrowserBailout` without also invoking
`onError`. A bailout outside Suspense remains fatal and reports only
through `onError`, with the original recoverable preserved as its cause,
while directly throwing the value continues to behave like a normal
render error.

Plumbs the option through the streaming, resume, and prerender entry
points for Node, browser, Edge, Bun, FB, markup, and noop renderers
while preserving the positional Fizz request API for callers that do not
expose the option.

Uses an environment-neutral browser-only rendering message for the
isomorphic `browser()` value and updates the production error mapping.
Tests cover successful browser bailouts, recoverable abort reasons,
root-fatal behavior, component stack information, the default noop
behavior, and direct throws in development and production.

DiffTrain build for [2042572](react@2042572)
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.

4 participants