React Hook for SSR Environment Detection
The `use-ssr` package provides a lightweight React hook designed to detect the execution environment (server-side, browser, or React Native) within React components and hooks. Currently at version 1.0.25, this library offers a stable API for conditionally rendering or executing logic based on where the React application is running. It differentiates itself with zero runtime dependencies beyond React itself, comprehensive TypeScript support, and specific flags for React Native. The hook returns boolean flags (`isBrowser`, `isServer`, `isNative`) and an enum `device` string, along with capabilities like `canUseWorkers`, `canUseEventListeners`, and `canUseViewport`, making it versatile for isomorphic React applications. Its primary use case is in Universal or Server-Side Rendered (SSR) applications, such as those built with Next.js, where conditional logic based on the environment is critical for optimal performance and correct behavior.
Common errors
-
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause The `useSSR` hook was called outside of a functional React component or another custom hook.fixRefactor your code to ensure `useSSR` is invoked only within the top level of a functional React component or a custom hook, following React's Rules of Hooks. -
ReferenceError: window is not defined
cause Browser-specific global objects like `window` or `document` were accessed in code that executed on the server-side.fixWrap any code that accesses `window`, `document`, or other browser-only globals within a conditional check using `isBrowser` from `useSSR`, or within a `useEffect` hook. Example: `if (isBrowser) { window.alert('Hello'); }` or `useEffect(() => { if (isBrowser) { console.log(window.innerWidth); } }, [isBrowser]);`. -
TypeError: Cannot destructure property 'isBrowser' of 'useSSR(...)' as it is undefined.
cause Attempting to named-import `useSSR` when it is a default export, or a bundler configuration issue preventing correct module resolution.fixEnsure `useSSR` is imported as a default export: `import useSSR from 'use-ssr';`. This package exports `useSSR` as a default, not a named export.
Warnings
- gotcha Like all React Hooks, `useSSR` must be called consistently within the render phase of a functional component or another custom hook. Violating the Rules of Hooks (e.g., calling it conditionally or outside a React function component) will lead to errors.
- gotcha When using `useSSR` to conditionally render content in an SSR application, be mindful of hydration mismatches. If the server renders one version of HTML (e.g., based on `isServer`) and the client attempts to hydrate with different content (e.g., based on `isBrowser`) before the client-side JavaScript takes over, React may issue a hydration warning or re-render the entire component tree on the client, potentially impacting performance and SEO.
- gotcha While `useSSR` correctly identifies the environment, directly accessing global browser-specific objects (like `window`, `document`, `localStorage`) outside of effects or `useSSR`'s returned flags in server-rendered components will still cause 'ReferenceError: window is not defined' errors during SSR.
Install
-
npm install use-ssr -
yarn add use-ssr -
pnpm add use-ssr
Imports
- useSSR
import { useSSR } from 'use-ssr'import useSSR from 'use-ssr'
Quickstart
import React from 'react';
import useSSR from 'use-ssr';
function App() {
const { isBrowser, isServer, isNative, device } = useSSR();
// This console log will show different results depending on the environment.
// In a browser: IS BROWSER: 👍, IS SERVER: 👎, IS NATIVE: 👎, DEVICE: browser
// On a server: IS BROWSER: 👎, IS SERVER: 👍, IS NATIVE: 👎, DEVICE: server
console.log('IS BROWSER: ', isBrowser ? '👍' : '👎');
console.log('IS SERVER: ', isServer ? '👍' : '👎');
console.log('IS NATIVE: ', isNative ? '👍' : '👎');
console.log('DEVICE: ', device);
return (
<div>
<h1>Environment Detection with useSSR</h1>
<p>Am I in a browser? {isBrowser ? 'Yes 👍' : 'No 👎'}</p>
<p>Am I on the server? {isServer ? 'Yes 👍' : 'No 👎'}</p>
<p>Am I in React Native? {isNative ? 'Yes 👍' : 'No 👎'}</p>
<p>Current device type: {device}</p>
{isBrowser && <p>This content only renders in the browser.</p>}
{isServer && <p>This content only renders on the server.</p>}
</div>
);
}
// To run this in a simple SSR setup (e.g., with Next.js or a custom server rendering),
// you would integrate this App component into your server-side render function.
// For a client-side only app, you'd render it directly to the DOM.
// Example for a basic client-side render:
// import ReactDOM from 'react-dom/client';
// const root = ReactDOM.createRoot(document.getElementById('root'));
// root.render(<App />);
export default App;