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 `