diff --git a/website/docs/advanced/ssg.mdx b/website/docs/advanced/ssg.mdx
index 7fd0724ece35..3c7a993d65e3 100644
--- a/website/docs/advanced/ssg.mdx
+++ b/website/docs/advanced/ssg.mdx
@@ -177,18 +177,103 @@ While you may expect that `BrowserOnly` hides away the children during server-si
### `useIsBrowser` {#useisbrowser}
-You can also use the `useIsBrowser()` hook to test if the component is currently in a browser environment. It returns `false` in SSR and `true` is CSR, after first client render. Use this hook if you only need to perform certain conditional operations on client-side, but not render an entirely different UI.
+Returns `true` when the React app has successfully hydrated in the browser.
+
+:::caution
+
+Use this hook instead of `typeof windows !== 'undefined'` in React rendering logic because `window` may be defined but hydration may not necessarily have been completed yet.
+
+The first client-side render output (in the browser) **must be exactly the same** as the server-side render output (Node.js). Not following this rule can lead to unexpected hydration behaviors, as described in [The Perils of Rehydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/).
+
+:::
+
+Usage example:
```jsx
+import React from // useEffect // useState,
+'react';
import useIsBrowser from '@docusaurus/useIsBrowser';
-function MyComponent() {
+const isFetchingLocationMessage = 'fetching location...';
+
+const MyComponent = () => {
+ // highlight-start
+ // Recommended
+ const isBrowser = useIsBrowser();
+ const location = isBrowser ? window.location.href : isFetchingLocationMessage;
+
+ // Not Recommended
+ // using typeof window !== 'undefined' will still work in this example
+ // but not recommended as it may cause issues depending on your business logic
+ const isWindowDefined = typeof window !== 'undefined';
+ const thisWillWorkButNotRecommended = isWindowDefined
+ ? window.location.href
+ : isFetchingLocationMessage;
+
+ // highlight-end
+ return (
+
+ {/* Recommended */}
+ {location}
+
+ {/* Not Recommended */}
+ {thisWillWorkButNotRecommended}
+
+ );
+};
+```
+
+:::caution If your business logic in the component relies on browser specifics to be functional at all, we recommend using [``](../docusaurus-core.mdx#browseronly). The following example will cause business logic issues when used with `useIsBrowser`:
+
+```jsx
+import React, {useState} from 'react';
+import useIsBrowser from '@docusaurus/useIsBrowser';
+
+const isFetchingLocationMessage = 'fetching location...';
+
+const MyComponent = () => {
const isBrowser = useIsBrowser();
- const location = isBrowser ? window.location.href : 'fetching location...';
- return {location};
+ const location = isBrowser ? window.location.href : isFetchingLocationMessage;
+ // highlight-start
+ const [isFetchingLocation, setIsFetchingLocation] = useState(
+ location === isFetchingLocationMessage,
+ );
+
+ // highlight-end
+ return (
+
+ {/*
+ This will always print true and will not update.
+ Component already rendered once and useState referenced the initial value as `true`
+ */}
+ {isFetchingLocation}
+
+ );
+};
+```
+
+To solve this, you can add a `useEffect` to run when hydration has completed:
+
+```js
+useEffect(() => {
+ setIsFetchingLocation(location === isFetchingLocationMessage);
+}, [isBrowser]);
+```
+
+Or use [``](../docusaurus-core.mdx#browseronly):
+
+```
+const ParentComponent = () => {
+ return (
+ Loading...}>
+ {() => }
+
+ )
}
```
+:::
+
### `useEffect` {#useeffect}
Lastly, you can put your logic in `useEffect()` to delay its execution until after first CSR. This is most appropriate if you are only performing side-effects but don't _get_ data from the client state.
diff --git a/website/docs/docusaurus-core.mdx b/website/docs/docusaurus-core.mdx
index 7e6615d8841e..f7847cace2e4 100644
--- a/website/docs/docusaurus-core.mdx
+++ b/website/docs/docusaurus-core.mdx
@@ -413,7 +413,7 @@ Returns `true` when the React app has successfully hydrated in the browser.
:::caution
-Use this hook instead of `typeof windows !== 'undefined'` in React rendering logic.
+Use this hook instead of `typeof windows !== 'undefined'` in React rendering logic because `window` may be defined but hydration may not necessarily have been completed yet.
The first client-side render output (in the browser) **must be exactly the same** as the server-side render output (Node.js). Not following this rule can lead to unexpected hydration behaviors, as described in [The Perils of Rehydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/).
@@ -427,9 +427,24 @@ import useIsBrowser from '@docusaurus/useIsBrowser';
const MyComponent = () => {
// highlight-start
+ // Recommended
const isBrowser = useIsBrowser();
+
+ // Not Recommended
+ // using typeof window !== 'undefined' will lead to mismatching render output
+ const isWindowDefined = typeof window !== 'undefined';
// highlight-end
- return {isBrowser ? 'Client' : 'Server'}
;
+ return (
+
+ {/* Recommended */}
+ {isBrowser ? 'Client (hydration completed)' : 'Server'}
+
+ {/* Not Recommended */}
+ {isWindowDefined
+ ? 'Client (hydration NOT completed, will mismatch)'
+ : 'Server'}
+
+ );
};
```