No SSR
Use browser instead whenever you can. noSSR predates React’s own solution to this problem, and it is only a workaround: it bails out by throwing an error that the server renderer, the browser, and your framework all have to be taught to recognize as deliberate.
React DOM now ships an official API use(browser()) for the same use case, and foxact has since shipped foxact/browser (which is a re-export and automatically falls back to noSSR-based polyfill) that is safe for you to adopt today even on React 18.
noSSR itself remains supported for backward compatibility.
Force React stream server-side rendering to include the closet <Suspense> boundary’s fallback UI (e.g. a loading indicator) into the generated server HTML. On the browser, the user will see the fallback UI first when the page loads, while React attempts to render the same component again during the hydration, and the user will see the proper content once React finishes the hydration. See <Suspense> Providing a fallback for server errors and client-only content at React docs.
The noSSR function can only be used with React stream server-side rendering. If you are using the React synchronous server-side rendering (e.g. renderToString), you should use the useIsClient approach instead. See useIsClient.
Usage
import { noSSR } from 'foxact/no-ssr';
function Chat() {
noSSR();
// ...
return (
// ...
);
}<Suspense fallback={<Loading />}>
<Chat />
</Suspense>The server HTML will include the loading indicator. It will be replaced by the <Chat /> component on the client.
Note that if you are calling hydrationRoot yourself and you are using the onRecoverableError option, you should add the following codes to your callback:
import { hydrateRoot } from 'react-dom/client';
import { App } from './app';
const el = document.getElementById('root');
if (el) {
const root = hydrateRoot(el, <App />, {
onRecoverableError(incomingError) {
if ('recoverableError' in incomingError && incomingError.recoverableError === 'NO_SSR') {
// Ignoring the error generated by `noSSR()`.
return;
}
// handle other errors
// ...
}
});
}
// patch console.error to prevent the recoverable error from being called
const origConsoleError = window.console.error;
const isRecoverableError = (error) => {
return error && typeof error === 'object' && 'recoverableError' in error && error.recoverableError
};
window.console.error = (...args) => {
if (isRecoverableError(args[0])) {
return;
}
origConsoleError.apply(window.console, args);
};
window.addEventListener('error', (ev) => {
if (isRecoverableError(ev.error)) {
ev.preventDefault();
return;
}
});