|
| 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). |
0 commit comments