useIsMounted
Prompt
Create a custom hook useIsMounted that returns false on the initial server-side render and true after the component has mounted on the client. This lets you safely use browser-only APIs like window, document, and localStorage.
Example
function ClientOnlyComponent() {
const isMounted = useIsMounted();
if (!isMounted) {
return null; // Don't render anything during SSR
}
return (
<div>
This component only renders on the client side!
{/* Now it's safe to access window, document, localStorage, etc. */}
</div>
);
}Playground
Use useState to create a state variable that will track whether the component has mounted:
const [isMounted, setIsMounted] = useState(false);Use useEffect with an empty dependency array to run code only after the first render is complete (i.e., when the component has mounted):
useEffect(() => {
// This code only runs on the client after the first render
}, []);Solution
Explanation
This hook is tiny, but it solves a real problem that comes up constantly when you're working with server-side rendering (SSR) in frameworks like Next.js.
export function useIsMounted() {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
return isMounted;
}Three lines of actual logic. Here's why it works:
useEffect only runs on the client. It never runs on the server. So the flow looks like this:
- On the server:
useState(false)returnsfalse.useEffectdoesn't run. Hook returnsfalse. - First render on the client: Same thing. Returns
false. This matches the server's output, which keeps React happy (no hydration mismatch). - After the component mounts:
useEffectfires, sets state totrue, and React re-renders. Now the hook returnstrue.
When you actually need this
If you're building with Next.js (or any SSR framework), your React components first run on the server to generate HTML. The problem is that browser APIs like window, document, localStorage, and navigator don't exist on the server. If your component tries to use them, it crashes.
This hook lets you safely wait until you're on the client:
const isMounted = useIsMounted();
if (!isMounted) return <Skeleton />;
// Now safe to use window, localStorage, etc.Why not just check typeof window?
You might think typeof window !== 'undefined' is simpler. And it does work technically. But it can cause a subtle bug: the server renders with false (no window), the client's first render sees true (window exists), and now the server and client outputs don't match. React calls this a "hydration mismatch" and warns about it.
useIsMounted avoids this because both server and client produce the same output on the first render (false). The switch to true happens in a second render, after hydration is complete. Clean and consistent.