"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; }