diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md
new file mode 100644
index 00000000000..2e0c2abfc07
--- /dev/null
+++ b/src/content/reference/react-dom/browser.md
@@ -0,0 +1,292 @@
+---
+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 render part of a React tree only 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 leaves the closest [``](/reference/react/Suspense) boundary's fallback in its place. In the browser, `use(browser())` returns `undefined`, so the component renders normally.
+
+[See more examples below.](#usage)
+
+#### Parameters {/*parameters*/}
+
+* **optional** `reason`: A string or function that explains why the content needs to render in the browser. If you pass a function, React calls it each time a server renderer encounters the value returned by `browser`. React does not call it in the browser. Use a function for values that are expensive to create, such as `() => new Error(...)`. The string or the function's return value becomes the `cause` of the `Error` passed to `onBrowserBailout`.
+
+#### Returns {/*returns*/}
+
+`browser` returns a value that you can pass to `use` in a component or use 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*/}
+
+* `use(browser())` must be inside a `` boundary during server rendering. Without one, the server render fails.
+* `browser` is not available in a `react-server` environment. You can use it while rendering Client Components on the server, but you cannot import it in a [React Server Component](/reference/rsc/server-components).
+* Calling `browser()` by itself has no effect. You can create the value at module scope and reuse it.
+* To skip rendering a component on the server, pass the value returned by `browser` to `use`. Do not throw it.
+
+---
+
+## 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:
+
+Press **Render on the server** to see the fallback first. The demo waits briefly before hydrating and showing the browser-only editor.
+
+
+
+```js src/App.js active
+import { Suspense, use } from 'react';
+import { browser } from 'react-dom';
+
+function BrowserOnlyEditor() {
+ use(browser('The editor requires browser APIs.'));
+ return ;
+}
+
+export default function App() {
+ return (
+ Loading editor...
+
+
+
+ );
+}
+```
+
+```js src/index.js
+import { hydrateRoot } from 'react-dom/client';
+import { renderToReadableStream } from 'react-dom/server';
+import Document from './Document.js';
+import { flushReadableStreamToFrame } from './demo-helpers.js';
+import './styles.css';
+
+async function main(frame) {
+ const stream = await renderToReadableStream();
+ await flushReadableStreamToFrame(stream, frame);
+
+ // Wait so both the fallback and hydrated content are visible.
+ await new Promise(resolve => setTimeout(resolve, 1200));
+ hydrateRoot(frame.contentDocument, );
+}
+
+const renderButton = document.getElementById('render');
+renderButton.addEventListener('click', () => {
+ renderButton.disabled = true;
+ main(document.getElementById('preview'));
+}, { once: true });
+```
+
+```js src/demo-helpers.js hidden
+export async function flushReadableStreamToFrame(readable, frame) {
+ const doc = frame.contentWindow.document;
+ const decoder = new TextDecoder();
+ for await (const chunk of readable) {
+ doc.write(decoder.decode(chunk, { stream: true }));
+ }
+ doc.close();
+}
+```
+
+```html public/index.html
+
+
+
+
+ Browser-only rendering
+
+
+
+
+
+
+
+```
+
+```css src/styles.css hidden
+iframe {
+ width: 100%;
+ height: 180px;
+ border: 1px solid #aaa;
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "canary",
+ "react-dom": "canary",
+ "react-scripts": "latest"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
+```
+
+
+
+
+
+In a React Server Components app, `use(browser())` must be called from a Client Component. If your framework uses Server Components by default, add the [`'use client'`](/reference/rsc/use-client) directive to that file or move the call to a child Client Component:
+
+```js {1}
+'use client';
+
+import { use } from 'react';
+import { browser } from 'react-dom';
+
+export default function BrowserOnlyEditor() {
+ use(browser('The editor requires browser APIs.'));
+ return ;
+}
+```
+
+
+
+---
+
+### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/}
+
+Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, you can wrap a Suspense-enabled data-fetching library's `useQuery` and skip server rendering when initial 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 `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, 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*/}
+
+Pass an `onBrowserBailout` callback to the server renderer to report browser-only rendering. When React leaves a Suspense fallback for the browser, it does not call the server renderer's `onError` callback or [`hydrateRoot`'s `onRecoverableError`](/reference/react-dom/client/hydrateRoot#error-logging-in-production) callback. This example also passes a reason, which is 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 you passed a reason to `browser`, it is available as the error's `cause`.
+2. An `errorInfo` object with a `componentStack` showing where browser-only rendering occurred.
+
+The reason function can return any value. Return a new `Error` to give the cause its own stack without creating the `Error` 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 usual error callbacks instead of `onBrowserBailout`.
+
+---
+
+### Aborting pending server rendering for the browser {/*aborting-pending-server-rendering-for-the-browser*/}
+
+Pass the value returned by `browser` as the reason when aborting a server render. React then leaves pending Suspense boundaries in their fallback state and renders 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);
+ }
+});
+```
+
+A `browser` abort reason does not trigger the server renderer's `onError` callback or `hydrateRoot`'s `onRecoverableError` callback. Instead, the server renderer reports each recovered Suspense boundary to `onBrowserBailout`.
+
+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..b16d0ee71c8 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 render part of a React tree only 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 `