Responsive Design in React
In this page:
CSS Media Queries Still Come First
For most responsive styling — different paddings, font sizes, or layouts at different widths — plain CSS media queries remain the right tool, since they're handled natively by the browser without any JavaScript overhead or re-renders.
Note: Reach for a React-based responsive hook only when you need to render fundamentally DIFFERENT components (not just different styles) at different sizes.
Warning: Reimplementing what a CSS media query could do in JavaScript adds unnecessary re-renders and complexity for no real benefit.
Detecting Screen Size in JavaScript with a Hook
When you genuinely need to render different components based on screen size (like a mobile hamburger menu vs. a full desktop nav), a useWindowWidth-style hook tracks the current width in React state, updating on the browser's resize event.
Note: Debounce the resize handler (see the useDebounce pattern from earlier) if you're doing expensive work on every resize event.
Warning: Always remove the resize event listener in the effect's cleanup function, or it leaks with every remount of the component using this hook.
Example: Detecting Screen Size in JavaScript with a Hook
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function useWindowWidth() {
const [width, setWidth] = React.useState(window.innerWidth);
React.useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return width;
}
function App() {
const width = useWindowWidth();
return <p>{width < 768 ? "Mobile layout" : "Desktop layout"}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Using matchMedia for a Specific Breakpoint
window.matchMedia(query) checks whether a specific media query currently matches, and can notify your code when that match status changes — a more targeted approach than tracking the raw pixel width when you only care about crossing one specific breakpoint.
Note: matchMedia is often more efficient than a resize listener when you only care about a boolean 'did we cross this one breakpoint' question.
Warning: Remember to remove the matchMedia change listener in cleanup too, exactly like a resize listener.
Example: Using matchMedia for a Specific Breakpoint
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function useIsMobile() {
const [isMobile, setIsMobile] = React.useState(window.matchMedia("(max-width: 767px)").matches);
React.useEffect(() => {
const mql = window.matchMedia("(max-width: 767px)");
const handler = e => setIsMobile(e.matches);
mql.addEventListener("change", handler);
return () => mql.removeEventListener("change", handler);
}, []);
return isMobile;
}
function App() {
const isMobile = useIsMobile();
return <p>{isMobile ? "Showing mobile nav" : "Showing desktop nav"}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Trying to detect screen size only once on mount, missing later resize events entirely.
- Using JavaScript-based responsive logic for things plain CSS media queries could handle more efficiently.
- Forgetting to clean up the resize event listener, leaking it on every remount.
- Most responsive behavior should still come from CSS media queries — React doesn't need to reinvent that.
- A useWindowWidth-style hook is useful specifically when RENDERING DIFFERENT COMPONENTS (not just different styles) per screen size.
- The window.matchMedia API lets JavaScript react to a specific media query matching or not.
- Always clean up resize/matchMedia listeners in useEffect's cleanup function.
matchMedia and resize events are supported in all modern browsers; no specific React version requirement.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: