From a17d58c82ca37bb8abf77ceefd51c06859af1a7f Mon Sep 17 00:00:00 2001
From: Josh Story
Date: Fri, 7 Aug 2026 21:28:16 -0700
Subject: [PATCH] Document browser-only rendering
Add the Canary browser API reference, including optional lazy reasons, server bailout reporting, fatal and abort behavior, navigation entries, and onBrowserBailout options for every streaming, resume, and prerender API that supports it.
---
src/content/reference/react-dom/browser.md | 179 ++++++++++++++++++
src/content/reference/react-dom/index.md | 6 +
.../server/renderToPipeableStream.md | 1 +
.../server/renderToReadableStream.md | 1 +
.../reference/react-dom/server/resume.md | 1 +
.../server/resumeToPipeableStream.md | 1 +
.../reference/react-dom/static/prerender.md | 1 +
.../react-dom/static/prerenderToNodeStream.md | 1 +
.../react-dom/static/resumeAndPrerender.md | 1 +
.../static/resumeAndPrerenderToNodeStream.md | 1 +
src/sidebarReference.json | 5 +
11 files changed, 198 insertions(+)
create mode 100644 src/content/reference/react-dom/browser.md
diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md
new file mode 100644
index 00000000000..5abc7551908
--- /dev/null
+++ b/src/content/reference/react-dom/browser.md
@@ -0,0 +1,179 @@
+---
+title: browser
+version: canary
+---
+
+
+
+**The `browser` API is currently only available in React’s Canary and Experimental channels.**
+
+[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
+
+
+
+
+
+`browser` lets you skip rendering part of a React tree on the server, leaving its nearest Suspense fallback in place until that content renders in the browser.
+
+```js
+use(browser(reason?));
+```
+
+
+
+
+
+---
+
+## Reference {/*reference*/}
+
+### `browser(reason?)` {/*browser*/}
+
+Call `browser` inside [`use`](/reference/react/use) to skip rendering a component on the server and render it in the browser instead:
+
+```js
+import { use } from 'react';
+import { browser } from 'react-dom';
+
+function BrowserOnly() {
+ use(browser('This component requires browser APIs.'));
+ return ;
+}
+```
+
+During server rendering, `use(browser())` stops rendering the component and renders the fallback of the closest [``](/reference/react/Suspense) boundary instead. In the browser, it has no effect, so the component renders normally.
+
+[See more examples below.](#usage)
+
+#### Parameters {/*parameters*/}
+
+* **optional** `reason`: A string or function that provides diagnostic information about why rendering should happen only in the browser. React calls a reason function each time a server renderer encounters the value returned by `browser`; it never calls it in the browser. Use a function for values that are expensive to create, such as `() => new Error(...)`. The resulting value becomes the `cause` of the `Error` passed to `onBrowserBailout`.
+
+#### Returns {/*returns*/}
+
+`browser` returns an opaque value. Pass this value to `use` in a component, or use it as the reason when [aborting a server render](#aborting-pending-server-rendering-for-the-browser). In the browser, passing this value to `use` returns `undefined`.
+
+#### Caveats {/*caveats*/}
+
+* A component that passes a value returned by `browser` to `use` during server rendering must have a `` boundary above it. Otherwise, the entire server render will fail.
+* `browser` is not available in a `react-server` environment. You can use it while server-rendering Client Components, but you cannot import it in a [React Server Component](/reference/rsc/server-components).
+* Calling `browser()` by itself does not check the current environment or affect rendering. To trigger its behavior, pass the return value to `use` or use it to abort a server render. This means you can create the value at module scope and reuse it.
+* To defer a component, pass the value returned by `browser` to `use`. Do not throw the value directly.
+
+---
+
+## Usage {/*usage*/}
+
+### Rendering content only in the browser {/*rendering-content-only-in-the-browser*/}
+
+Call `use` with the value returned by `browser` to skip rendering a component on the server:
+
+```js
+import { Suspense, use } from 'react';
+import { browser } from 'react-dom';
+
+function BrowserOnlyEditor() {
+ use(browser('The editor requires browser APIs.'));
+ return ;
+}
+
+export default function Page() {
+ return (
+ Loading editor...
}>
+
+
+ );
+}
+```
+
+During server rendering, React includes the `Loading editor...` fallback in the HTML. When the app renders in the browser, `use(browser())` continues immediately and React renders the `Editor` instead.
+
+---
+
+### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/}
+
+Like other calls to [`use`](/reference/react/use), `use(browser())` can be called conditionally, including inside a custom Hook. For example, you can wrap a Suspense-enabled data-fetching library's `useQuery` to render initial data on the server, but defer to the browser when that data is missing:
+
+```js {3}
+function useBrowserQuery(query, options) {
+ if (options.initialData === undefined) {
+ use(browser('useBrowserQuery: No initial data was provided.'));
+ }
+
+ return useQuery(query, options);
+}
+
+function ProductDetails({ productId, initialData }) {
+ const product = useBrowserQuery(`/api/products/${productId}`, {
+ initialData,
+ });
+
+ return {product.name}
;
+}
+```
+
+On the server, `useBrowserQuery` calls the underlying `useQuery` only when `initialData` is available. Otherwise, `use(browser())` leaves the nearest Suspense fallback in the HTML. In the browser, `use(browser())` continues immediately, so the query library can fetch the data or read it from its client cache.
+
+---
+
+### Reporting browser-only rendering on the server {/*reporting-browser-only-rendering-on-the-server*/}
+
+Provide `onBrowserBailout` to the server renderer to report browser-only rendering. React does not report a browser-only render recovered by a Suspense boundary to the server renderer's `onError` callback or [`hydrateRoot`'s `onRecoverableError`](/reference/react-dom/client/hydrateRoot#error-logging-in-production) callback. This example also passes an optional reason, which React makes available as the reported error's `cause`:
+
+```js
+import { Suspense, use } from 'react';
+import { browser } from 'react-dom';
+import { renderToPipeableStream } from 'react-dom/server';
+
+function BrowserOnlyEditor() {
+ use(browser(() => new Error('The editor requires a browser API.')));
+ return ;
+}
+
+const { pipe } = renderToPipeableStream(
+ Loading editor...}>
+
+ ,
+ {
+ onShellReady() {
+ pipe(response);
+ },
+ onBrowserBailout(error, errorInfo) {
+ logBrowserBailout(error, errorInfo);
+ }
+ }
+);
+```
+
+`onBrowserBailout` receives two arguments:
+
+1. An `Error` describing the browser-only render. If a reason was supplied to `browser`, it is available as the error's `cause`.
+2. An `errorInfo` object containing the `componentStack` of the browser-only render.
+
+The reason function can return any value. Returning a new `Error` gives the cause its own stack without creating that `Error` during rendering in the browser. React does not serialize the reason into the HTML.
+
+If there is no Suspense boundary to provide a fallback, the server render fails. React reports the failure through the renderer's normal error callbacks instead of `onBrowserBailout`.
+
+---
+
+### Aborting pending server rendering for the browser {/*aborting-pending-server-rendering-for-the-browser*/}
+
+You can pass the value returned by `browser` as the reason for aborting a server render. This leaves pending Suspense boundaries in their fallback state so React can render their content in the browser:
+
+```js {1,8}
+import { browser } from 'react-dom';
+import { renderToPipeableStream } from 'react-dom/server';
+
+const { pipe, abort } = renderToPipeableStream(, {
+ onShellReady() {
+ pipe(response);
+ setTimeout(() => {
+ abort(browser('The server render timed out.'));
+ }, 10000);
+ }
+});
+```
+
+Unlike other abort reasons, a value returned by `browser` is not reported to the server renderer's `onError` callback or to `hydrateRoot`'s `onRecoverableError` callback. The server renderer reports each recovered Suspense boundary to `onBrowserBailout` instead.
+
+For server rendering APIs that accept an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal), pass `browser()` as the reason to [`AbortController.abort`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort).
diff --git a/src/content/reference/react-dom/index.md b/src/content/reference/react-dom/index.md
index d01bd656204..527abba092b 100644
--- a/src/content/reference/react-dom/index.md
+++ b/src/content/reference/react-dom/index.md
@@ -30,6 +30,12 @@ These APIs can be used to make apps faster by pre-loading resources such as scri
* [`preinit`](/reference/react-dom/preinit) lets you fetch and evaluate an external script or fetch and insert a stylesheet.
* [`preinitModule`](/reference/react-dom/preinitModule) lets you fetch and evaluate an ESM module.
+## Server Rendering APIs {/*server-rendering-apis*/}
+
+This API controls how components render on the server:
+
+* [`browser`](/reference/react-dom/browser) lets you skip rendering part of a React tree on the server, leaving its nearest Suspense fallback in place until that content renders in the browser.
+
---
## Entry points {/*entry-points*/}
diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md
index 9668e01b9a7..5d48c6a66a3 100644
--- a/src/content/reference/react-dom/server/renderToPipeableStream.md
+++ b/src/content/reference/react-dom/server/renderToPipeableStream.md
@@ -56,6 +56,7 @@ On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to
* **optional** `namespaceURI`: A string with the root [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) for the stream. Defaults to regular HTML. Pass `'http://www.w3.org/2000/svg'` for SVG or `'http://www.w3.org/1998/Math/MathML'` for MathML.
* **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src).
* **optional** `onAllReady`: A callback that fires when all rendering is complete, including both the [shell](#specifying-what-goes-into-the-shell) and all additional [content.](#streaming-more-content-as-it-loads) You can use this instead of `onShellReady` [for crawlers and static generation.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) If you start streaming here, you won't get any progressive loading. The stream will contain the final HTML.
+ * **optional** `onBrowserBailout`: A callback React calls when it recovers from [`browser()`](/reference/react-dom/browser) by leaving a Suspense fallback for the browser to replace. It receives an `Error` describing the browser-only render and an `errorInfo` object containing the `componentStack`. If a reason was passed to `browser`, it is available as `error.cause`. By default, React does nothing. [See how to report browser-only rendering.](/reference/react-dom/browser#reporting-browser-only-rendering-on-the-server)
* **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`. You can also use it to [adjust the status code](#setting-the-status-code) before the shell is emitted.
* **optional** `onShellReady`: A callback that fires right after the [initial shell](#specifying-what-goes-into-the-shell) has been rendered. You can [set the status code](#setting-the-status-code) and call `pipe` here to start streaming. React will [stream the additional content](#streaming-more-content-as-it-loads) after the shell along with the inline `