browser - This feature is available in the latest Canary version of React

Canary

The browser API is currently only available in React’s Canary and Experimental channels.

Learn more about React’s release channels here.

browser lets you render part of a React tree only in the browser.

use(browser(reason?))

Reference

browser(reason?)

Call browser inside use to skip rendering a component on the server and render it in the browser instead:

import { use } from 'react';
import { browser } from 'react-dom';

function BrowserOnly() {
use(browser('This component requires browser APIs.'));
return <BrowserContent />;
}

During server rendering, use(browser()) stops rendering the component and leaves the closest <Suspense> boundary’s fallback in its place. In the browser, use(browser()) returns undefined, so the component renders normally.

See more examples below.

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

browser returns a value that you can pass to use in a component or use as the reason when aborting a server render. In the browser, passing this value to use returns undefined.

Caveats

  • use(browser()) must be inside a <Suspense> 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.
  • 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

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.

import { Suspense, use } from 'react';
import { browser } from 'react-dom';

function BrowserOnlyEditor() {
  use(browser('The editor requires browser APIs.'));
  return <label>Draft: <input /></label>;
}

export default function App() {
  return (
    <Suspense fallback={<p>Loading editor...</p>}>
      <BrowserOnlyEditor />
    </Suspense>
  );
}

Note

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' directive to that file or move the call to a child Client Component:

'use client';

import { use } from 'react';
import { browser } from 'react-dom';

export default function BrowserOnlyEditor() {
use(browser('The editor requires browser APIs.'));
return <Editor />;
}

Conditionally rendering in the browser

Like other calls to 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:

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 <h1>{product.name}</h1>;
}

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

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 callback. This example also passes a reason, which is available as the reported error’s cause:

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 <Editor />;
}

const { pipe } = renderToPipeableStream(
<Suspense fallback={<p>Loading editor...</p>}>
<BrowserOnlyEditor />
</Suspense>,
{
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

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:

import { browser } from 'react-dom';
import { renderToPipeableStream } from 'react-dom/server';

const { pipe, abort } = renderToPipeableStream(<App />, {
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, pass browser() as the reason to AbortController.abort.