Skip to content

Commit 47e6ef9

Browse files
committed
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.
1 parent c7d6b70 commit 47e6ef9

11 files changed

Lines changed: 202 additions & 3 deletions
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
title: browser
3+
version: canary
4+
---
5+
6+
<Canary>
7+
8+
**The `browser` API is currently only available in React’s Canary and Experimental channels.**
9+
10+
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
11+
12+
</Canary>
13+
14+
<Intro>
15+
16+
`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.
17+
18+
```js
19+
use(browser(reason?));
20+
```
21+
22+
</Intro>
23+
24+
<InlineToc />
25+
26+
---
27+
28+
## Reference {/*reference*/}
29+
30+
### `browser(reason?)` {/*browser*/}
31+
32+
Call `browser` inside [`use`](/reference/react/use) to defer rendering until the component runs in the browser:
33+
34+
```js
35+
import {use} from 'react';
36+
import {browser} from 'react-dom';
37+
38+
function BrowserOnly() {
39+
use(browser('This component requires browser APIs.'));
40+
return <ClientContent />;
41+
}
42+
```
43+
44+
During server rendering, `use(browser())` stops rendering the component and displays the fallback of the closest [`<Suspense>`](/reference/react/Suspense) boundary. During rendering in the browser, `use(browser())` continues immediately so the component can render.
45+
46+
[See more examples below.](#usage)
47+
48+
#### Parameters {/*parameters*/}
49+
50+
* **optional** `reason`: A string that describes why React should defer rendering, or a function that returns a diagnostic value. Use a function for values that are expensive to create, such as `() => new Error(...)`. React calls the function only when a server renderer consumes the value returned by `browser`, so the browser does not unnecessarily create the error or capture its stack. The server renderer attaches the resulting value as the `cause` of the `Error` passed to `onBrowserBailout`.
51+
52+
#### Returns {/*returns*/}
53+
54+
`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`.
55+
56+
#### Caveats {/*caveats*/}
57+
58+
* `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).
59+
* A component that passes a value returned by `browser` to `use` during server rendering must have a `<Suspense>` boundary above it. Otherwise, the entire server render will fail.
60+
* Calling `browser()` by itself does not check the current environment or affect rendering. The behavior depends on whether you pass its return value to `use` during server or browser rendering. This means you can create the value at module scope and reuse it.
61+
* To defer a component, pass the value returned by `browser` to `use`. Do not throw the value directly.
62+
63+
---
64+
65+
## Usage {/*usage*/}
66+
67+
### Rendering content only in the browser {/*rendering-content-only-in-the-browser*/}
68+
69+
Call `use` with the value returned by `browser` to skip rendering a component on the server:
70+
71+
```js
72+
import {Suspense, use} from 'react';
73+
import {browser} from 'react-dom';
74+
75+
function BrowserOnlyEditor() {
76+
use(browser('The editor requires browser APIs.'));
77+
return <Editor />;
78+
}
79+
80+
export default function Page() {
81+
return (
82+
<Suspense fallback={<p>Loading editor...</p>}>
83+
<BrowserOnlyEditor />
84+
</Suspense>
85+
);
86+
}
87+
```
88+
89+
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.
90+
91+
React treats this deferral as intentional. It does not report it to the server renderer's `onError` callback or [`hydrateRoot`'s `onRecoverableError`](/reference/react-dom/client/hydrateRoot#error-logging-in-production) callback.
92+
93+
---
94+
95+
### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/}
96+
97+
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 data-fetching library's `useQuery` to render initial data on the server, but defer to the browser when that data is missing:
98+
99+
```js {3}
100+
function useBrowserQuery(query, options) {
101+
if (options.initialData === undefined) {
102+
use(browser('No initial data was provided for this query.'));
103+
}
104+
105+
return useQuery(query, options);
106+
}
107+
108+
function ProductDetails({productId, initialData}) {
109+
const product = useBrowserQuery(`/api/products/${productId}`, {
110+
initialData,
111+
});
112+
113+
return <h1>{product.name}</h1>;
114+
}
115+
```
116+
117+
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.
118+
119+
---
120+
121+
### Reporting browser-only rendering on the server {/*reporting-browser-only-rendering-on-the-server*/}
122+
123+
Pass an optional reason to `browser` and provide `onBrowserBailout` to the server renderer to report browser-only rendering:
124+
125+
```js
126+
import {Suspense, use} from 'react';
127+
import {browser} from 'react-dom';
128+
import {renderToPipeableStream} from 'react-dom/server';
129+
130+
function BrowserOnlyEditor() {
131+
use(browser(() => new Error('The editor requires a browser API.')));
132+
return <Editor />;
133+
}
134+
135+
const {pipe} = renderToPipeableStream(
136+
<Suspense fallback={<p>Loading editor...</p>}>
137+
<BrowserOnlyEditor />
138+
</Suspense>,
139+
{
140+
onShellReady() {
141+
pipe(response);
142+
},
143+
onBrowserBailout(error, errorInfo) {
144+
logBrowserBailout(error.cause, errorInfo.componentStack);
145+
}
146+
}
147+
);
148+
```
149+
150+
When React successfully recovers by leaving a Suspense fallback for the browser to replace, `onBrowserBailout` receives two arguments:
151+
152+
1. An `Error` describing the browser-only render. Its stack points to the `use` or abort call that consumed the value, and its `cause` is the reason supplied to `browser`.
153+
2. An `errorInfo` object containing the `componentStack` of the browser-only render.
154+
155+
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 or report the bailout to a client callback.
156+
157+
If browser-only rendering prevents the server shell from completing because there is no Suspense boundary, React reports the failure to the server renderer's normal error handling callbacks instead of `onBrowserBailout`.
158+
159+
---
160+
161+
### Aborting pending server rendering for the browser {/*aborting-pending-server-rendering-for-the-browser*/}
162+
163+
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:
164+
165+
```js {1,8}
166+
import {browser} from 'react-dom';
167+
import {renderToPipeableStream} from 'react-dom/server';
168+
169+
const {pipe, abort} = renderToPipeableStream(<App />, {
170+
onShellReady() {
171+
pipe(response);
172+
setTimeout(() => {
173+
abort(browser('The server render timed out.'));
174+
}, 10000);
175+
}
176+
});
177+
```
178+
179+
Unlike aborting with an error, aborting with 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.
180+
181+
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).

src/content/reference/react-dom/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ These APIs can be used to make apps faster by pre-loading resources such as scri
3030
* [`preinit`](/reference/react-dom/preinit) lets you fetch and evaluate an external script or fetch and insert a stylesheet.
3131
* [`preinitModule`](/reference/react-dom/preinitModule) lets you fetch and evaluate an ESM module.
3232

33+
## Server Rendering APIs {/*server-rendering-apis*/}
34+
35+
This API controls how components render on the server:
36+
37+
* <CanaryBadge /> [`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.
38+
3339
---
3440

3541
## Entry points {/*entry-points*/}

src/content/reference/react-dom/server/renderToPipeableStream.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to
5656
* **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.
5757
* **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).
5858
* **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.
59+
* <CanaryBadge /> **optional** `onBrowserBailout`: A callback React calls when it successfully recovers from [`browser()`](/reference/react-dom/browser) by leaving a Suspense fallback for the browser to replace. It receives the generated `Error` and an `errorInfo` object containing the `componentStack`. The `Error` includes the optional reason passed to `browser` as its `cause`. By default, React does nothing. [See how to report browser-only rendering.](/reference/react-dom/browser#reporting-browser-only-rendering-on-the-server)
5960
* **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.
6061
* **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 `<script>` tags that replace the HTML loading fallbacks with the content.
6162
* **optional** `onShellError`: A callback that fires if there was an error rendering the initial shell. It receives the error as an argument. No bytes were emitted from the stream yet, and neither `onShellReady` nor `onAllReady` will get called, so you can [output a fallback HTML shell.](#recovering-from-errors-inside-the-shell)

src/content/reference/react-dom/server/renderToReadableStream.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to
5656
* **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as passed to [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters)
5757
* **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.
5858
* **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).
59+
* <CanaryBadge /> **optional** `onBrowserBailout`: A callback React calls when it successfully recovers from [`browser()`](/reference/react-dom/browser) by leaving a Suspense fallback for the browser to replace. It receives the generated `Error` and an `errorInfo` object containing the `componentStack`. The `Error` includes the optional reason passed to `browser` as its `cause`. By default, React does nothing. [See how to report browser-only rendering.](/reference/react-dom/browser#reporting-browser-only-rendering-on-the-server)
5960
* **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.
6061
* **optional** `progressiveChunkSize`: The number of bytes in a chunk. [Read more about the default heuristic.](https://github.com/react/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225)
6162
* **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.

src/content/reference/react-dom/server/resume.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ async function handler(request, writable) {
4848
* **optional** `options`: An object with streaming options.
4949
* **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).
5050
* **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.
51+
* <CanaryBadge /> **optional** `onBrowserBailout`: A callback React calls when it successfully recovers from [`browser()`](/reference/react-dom/browser) by leaving a Suspense fallback for the browser to replace. It receives the generated `Error` and an `errorInfo` object containing the `componentStack`. The `Error` includes the optional reason passed to `browser` as its `cause`. By default, React does nothing. [See how to report browser-only rendering.](/reference/react-dom/browser#reporting-browser-only-rendering-on-the-server)
5152
* **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-outside-the-shell) or [not.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](/reference/react-dom/server/renderToReadableStream#logging-crashes-on-the-server) make sure that you still call `console.error`.
5253
5354
@@ -233,4 +234,4 @@ export function sleep(timeoutMS) {
233234
### Further reading {/*further-reading*/}
234235
235236
Resuming behaves like `renderToReadableStream`. For more examples, check out the [usage section of `renderToReadableStream`](/reference/react-dom/server/renderToReadableStream#usage).
236-
The [usage section of `prerender`](/reference/react-dom/static/prerender#usage) includes examples of how to use `prerender` specifically.
237+
The [usage section of `prerender`](/reference/react-dom/static/prerender#usage) includes examples of how to use `prerender` specifically.

src/content/reference/react-dom/server/resumeToPipeableStream.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ async function handler(request, response) {
5151
* **optional** `options`: An object with streaming options.
5252
* **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).
5353
* **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.
54+
* <CanaryBadge /> **optional** `onBrowserBailout`: A callback React calls when it successfully recovers from [`browser()`](/reference/react-dom/browser) by leaving a Suspense fallback for the browser to replace. It receives the generated `Error` and an `errorInfo` object containing the `componentStack`. The `Error` includes the optional reason passed to `browser` as its `cause`. By default, React does nothing. [See how to report browser-only rendering.](/reference/react-dom/browser#reporting-browser-only-rendering-on-the-server)
5455
* **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-outside-the-shell) or [not.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](/reference/react-dom/server/renderToReadableStream#logging-crashes-on-the-server) make sure that you still call `console.error`.
5556
* **optional** `onShellReady`: A callback that fires right after the [shell](#specifying-what-goes-into-the-shell) has finished. You can 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 `<script>` tags that replace the HTML loading fallbacks with the content.
5657
* **optional** `onShellError`: A callback that fires if there was an error rendering the shell. It receives the error as an argument. No bytes were emitted from the stream yet, and neither `onShellReady` nor `onAllReady` will get called, so you can [output a fallback HTML shell](#recovering-from-errors-inside-the-shell) or use the prelude.
@@ -75,4 +76,4 @@ async function handler(request, response) {
7576
### Further reading {/*further-reading*/}
7677
7778
Resuming behaves like `renderToReadableStream`. For more examples, check out the [usage section of `renderToReadableStream`](/reference/react-dom/server/renderToReadableStream#usage).
78-
The [usage section of `prerender`](/reference/react-dom/static/prerender#usage) includes examples of how to use `prerenderToNodeStream` specifically.
79+
The [usage section of `prerender`](/reference/react-dom/static/prerender#usage) includes examples of how to use `prerenderToNodeStream` specifically.

0 commit comments

Comments
 (0)