"use client";
import {useEffect, useState} from "react";
const MOBILE_BREAKPOINT = 768;
type WindowSize = {
width: number | null;
height: number | null;
};
type HookReturnType = Readonly<{
windowSize: WindowSize;
isMobile: boolean;
isDesktop: boolean;
}>;
/**
* Client hook to get the window size and whether the window is mobile or desktop.
* @returns An object containing the window size and whether the window is mobile or desktop.
*@example
* ```tsx
* function MyComponent() {
* const { windowSize, isMobile, isDesktop } = useWindowSize();
*
* return (
*
*
Window Size: {windowSize.width} x {windowSize.height}
*
{isMobile ? 'Mobile View' : 'Desktop View'}
*
* );
* }
* ```
*/
export function useWindowSize(): HookReturnType {
const [windowSize, setWindowSize] = useState({
width: null,
height: null,
});
useEffect(() => {
/**
* This function updates the window size.
* It is created inside of useEffect and has a short lifespan.
* After it is attached to the window object, it will be garbage collected
* when the component unmounts or the window is resized.
*/
function __handleResize__() {
// eslint-disable-next-line react-x/set-state-in-effect -- syncing window size into state is the hook's purpose
setWindowSize({
width: globalThis.window.innerWidth,
height: globalThis.window.innerHeight,
});
}
globalThis.window.addEventListener("resize", __handleResize__);
__handleResize__(); // Call the function once to get the initial window size
return () => globalThis.window.removeEventListener("resize", __handleResize__);
}, []); // Empty array ensures that effect is only run on mount
return {
windowSize,
isMobile: typeof windowSize.width === "number" && windowSize.width < MOBILE_BREAKPOINT,
isDesktop: typeof windowSize.width === "number" && windowSize.width >= MOBILE_BREAKPOINT,
} as const;
}