Overview
The useWindowWidth hook provides real-time tracking of the browser window width. It automatically updates when the window is resized, making it perfect for responsive layouts and breakpoint-based logic.
Hook Signature
const windowWidth = useWindowWidth();
Return Value
The current width of the browser window in pixels. Updates automatically on window resize.
Usage Examples
Persistent Player
From /home/daytona/workspace/source/src/components/PersistentPlayer/PersistentPlayer.jsx:16:
import useWindowWidth from "../../hooks/useWindowWidth";
const PersistentPlayer = ({ onClose }) => {
const dispatch = useDispatch();
const audioRef = useRef(null);
const windowWidth = useWindowWidth();
const [volume, setVolume] = useState(() => {
const savedVolume = localStorage.getItem("nsnPlayerVolume");
return savedVolume ? parseFloat(savedVolume) : 1;
});
// Use windowWidth for responsive player layout
const isCompactMode = windowWidth < 768;
return (
<div className={isCompactMode ? styles.compact : styles.full}>
{/* Player controls */}
</div>
);
};
Responsive Breakpoints
const windowWidth = useWindowWidth();
const isMobile = windowWidth < 640;
const isTablet = windowWidth >= 640 && windowWidth < 1024;
const isDesktop = windowWidth >= 1024;
return (
<div>
{isMobile && <MobileLayout />}
{isTablet && <TabletLayout />}
{isDesktop && <DesktopLayout />}
</div>
);
Dynamic Column Layout
const windowWidth = useWindowWidth();
const getColumnCount = () => {
if (windowWidth < 640) return 1;
if (windowWidth < 1024) return 2;
if (windowWidth < 1536) return 3;
return 4;
};
const columns = getColumnCount();
return (
<div style={{ gridTemplateColumns: `repeat(${columns}, 1fr)` }}>
{/* Grid items */}
</div>
);
Conditional Feature Display
const windowWidth = useWindowWidth();
const showSidebar = windowWidth >= 1024;
const showExpandedPlayer = windowWidth >= 768;
return (
<div className={styles.container}>
<main>{/* Main content */}</main>
{showSidebar && <aside>{/* Sidebar */}</aside>}
<footer>
{showExpandedPlayer ? <ExpandedPlayer /> : <MinimalPlayer />}
</footer>
</div>
);
Implementation Details
Complete Source Code
import { useState, useEffect } from "react";
const useWindowWidth = () => {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return windowWidth;
};
export default useWindowWidth;
Initial State
The hook initializes with the current window width:
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
Resize Handling
A resize event listener updates the width whenever the window is resized:
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
window.addEventListener("resize", handleResize);
Cleanup
The event listener is properly cleaned up when the component unmounts:
return () => {
window.removeEventListener("resize", handleResize);
};
Common Breakpoints
Standard responsive breakpoints to use with useWindowWidth:
| Breakpoint | Width | Use Case |
|---|
| Mobile | < 640px | Small phones |
| Mobile Large | < 768px | Large phones |
| Tablet | 768px - 1024px | Tablets |
| Desktop | 1024px - 1536px | Small desktops |
| Desktop Large | > 1536px | Large screens |
Best Practices
Define breakpoint constants to keep your responsive logic consistent across components:const BREAKPOINTS = {
mobile: 640,
tablet: 768,
desktop: 1024,
wide: 1536
};
const windowWidth = useWindowWidth();
const isMobile = windowWidth < BREAKPOINTS.mobile;
Consider debouncing resize handlers if you’re performing expensive calculations based on window width. The hook itself is lightweight, but your derived logic might benefit from debouncing.
Avoid using this hook in many components simultaneously. Consider using a global state management solution or context if you need window width in multiple places to prevent duplicate event listeners.
Optimization with useMemo
import { useMemo } from 'react';
const windowWidth = useWindowWidth();
const layoutConfig = useMemo(() => ({
columns: windowWidth < 640 ? 1 : windowWidth < 1024 ? 2 : 3,
showSidebar: windowWidth >= 1024,
playerHeight: windowWidth < 768 ? 60 : 80
}), [windowWidth]);
Debouncing Resize Events
For expensive operations, consider creating a debounced version:
import { useState, useEffect } from "react";
const useDebouncedWindowWidth = (delay = 150) => {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
useEffect(() => {
let timeoutId;
const handleResize = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
setWindowWidth(window.innerWidth);
}, delay);
};
window.addEventListener("resize", handleResize);
return () => {
clearTimeout(timeoutId);
window.removeEventListener("resize", handleResize);
};
}, [delay]);
return windowWidth;
};
Comparison with useMobileDetect
| Feature | useWindowWidth | useMobileDetect |
|---|
| Detection Method | Window width | User agent |
| Granularity | Exact pixel value | Boolean (mobile/not mobile) |
| Breakpoint Control | Full control | Fixed detection |
| Updates On | Window resize | Initial mount + resize |
| Best For | Responsive layouts | Device-specific logic |
Use useWindowWidth when you need precise control over breakpoints. Use useMobileDetect when you need to know if the device is actually mobile (for native features or mobile-specific UX).