11import React from 'react' ;
22
33/**
4- * Returns the width and height of the window.
4+ * Reads the current window dimensions.
5+ *
6+ * Only invoked on the client - from the mount effect and the resize handler
7+ * in `useWindowDimensions`, never during render. The `typeof window` guard is
8+ * a defensive net so a stray render-time call can never throw a
9+ * `ReferenceError` under SSR / static prerender.
510 *
611 * @returns {WindowDimensions } { width, height }
712 */
813function getWindowDimensions ( ) : WindowDimensions {
14+ if ( typeof window === 'undefined' ) {
15+ return { width : 0 , height : 0 } ;
16+ }
917 const { innerWidth : width , innerHeight : height } = window ;
1018 return {
1119 width,
@@ -16,12 +24,25 @@ function getWindowDimensions(): WindowDimensions {
1624/**
1725 * Custom hook for getting window dimensions.
1826 *
27+ * State is initialised to zeroed dimensions rather than by reading `window`
28+ * in the `useState` initialiser. This keeps the server render and the first
29+ * client (hydration) render identical - reading `window` during render would
30+ * make them diverge (`0x0` on the server vs the real size on the client) and
31+ * trigger a hydration mismatch. The real dimensions are read once on mount
32+ * via the effect below and then kept current by the debounced resize listener.
33+ *
1934 * @returns {WindowDimensions } { width, height }
2035 */
2136export function useWindowDimensions ( ) : WindowDimensions {
22- const [ windowDimensions , setWindowDimensions ] = React . useState ( getWindowDimensions ( ) ) ;
37+ const [ windowDimensions , setWindowDimensions ] = React . useState < WindowDimensions > ( {
38+ width : 0 ,
39+ height : 0
40+ } ) ;
2341
2442 React . useEffect ( ( ) => {
43+ // Sync to the real dimensions on mount (client only).
44+ setWindowDimensions ( getWindowDimensions ( ) ) ;
45+
2546 let resizeTimeout : number ;
2647
2748 function handleResize ( ) {
0 commit comments