useIsMounted
Prompt
Create a custom React hook called useIsMounted
that determines if a component has been mounted to the DOM. This is particularly useful in server-side rendered (SSR) applications.
This hook helps developers distinguish between server-side and client-side rendering, allowing them to safely use client/browser only APIs like window
, document
, localStorage
, etc.
Requirements
The hook should return a boolean indicating whether the component has mounted
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
The useIsMounted
hook is a simple yet powerful tool for managing the differences between server-side and client-side rendering in React applications. Let's break down how it works:
First, we create a state variable using the useState
hook to track whether the component has mounted:
const [isMounted, setIsMounted] = useState(false);
We initialize the state to false
, assuming that when the component first renders (which could be on the server), it has not yet mounted to the DOM.
Next, we use the useEffect
hook to update this state once the component has actually mounted:
useEffect(() => {
setIsMounted(true);
}, []);
The empty dependency array []
ensures that this effect only runs once, immediately after the component's first render is committed to the DOM. This effect will never run during server-side rendering since useEffect
callbacks are only executed on the client.
Finally, we return the boolean state:
return isMounted;
During server-side rendering, this hook will always return false
. On the client, it will return false
for the first render, then true
for all subsequent renders.
Why This Pattern Matters
This pattern is particularly important for server-side rendered (SSR) applications built with frameworks like Next.js. In SSR, your React components run in two different environments:
- First on the server to generate the initial HTML
- Then on the client to "hydrate" that HTML with event listeners and React's reconciliation
During server rendering, browser APIs like window
, document
, localStorage
, and many others are not available. If you try to access them, your application will crash. The useIsMounted
hook provides a safe way to conditionally render components that rely on browser-only APIs.
Using With Client-Only Hooks and APIs
The useIsMounted
hook is especially useful for safely using other hooks that depend on browser APIs, such as:
useMediaQuery
(which useswindow.matchMedia
)useLocalStorage
(which useswindow.localStorage
)useWindowSize
(which depends onwindow
dimensions)- Third-party hooks that use browser-specific features
Instead of having numerous checks for browser availability throughout your code, you can use this pattern:
function MyComponent() {
const isMounted = useIsMounted();
// Only run the client-specific hooks when mounted
const theme = isMounted ? useLocalStorage('theme', 'light') : ['light', () => {}];
const isMobile = isMounted ? useMediaQuery('(max-width: 768px)') : false;
// Render responsibly based on mounting state
if (!isMounted) {
return <FallbackComponent />; // A simple placeholder during SSR
}
return (
// Component with full client-side functionality
);
}
Avoiding Common Pitfalls
Some developers attempt to detect server vs. client environments by directly checking for the existence of browser APIs:
// 🚫 Bad practice
const isBrowser = typeof window !== 'undefined';
While this works technically, it can lead to subtle bugs due to "hydration mismatches" between server and client renders. React expects the initial client render to produce the same UI as what the server rendered; otherwise, it warns about a "hydration mismatch."
Using the useIsMounted
hook forces components to render consistently across environments while still providing a way to perform client-only logic after mounting.