Using Material UI's useMediaQuery Without Layout Shifts
Use Material UI's useMediaQuery hook to tie responsive components to theme breakpoints, with SSR guards and verification steps.
03 Sept 2025, 01:58 UTC

The problem: responsive UI that changes after the page loads
You hide a navigation drawer below the sm breakpoint. On the server, the drawer renders. Once the browser hydrates the React tree, Material UI's useMediaQuery hook evaluates the actual viewport and removes the drawer. The result is a visible layout shift and, in some cases, a hydration mismatch warning.
The useful takeaway is simple: useMediaQuery connects your component to the theme's breakpoint definitions instead of hardcoded pixel values, but it returns undefined on the server until the client measures the window. You need a hydration guard and a verification routine.
How the hook fits Material UI breakpoints
Material UI themes define named breakpoints such as xs, sm, md, lg, and xl. A breakpoint is a minimum viewport width at which the layout changes. The useMediaQuery hook accepts one of these names or a custom media query string and returns a boolean: true when the viewport matches, false otherwise.
Because it is a React hook, you call it inside a functional component or a custom hook. It subscribes to window resize events and updates the component when the match status changes.
Worked example: conditionally render a sidebar
This example assumes Material UI v5 and React 18. Run the following in your project's terminal:
npm install @mui/material @emotion/react @emotion/styled
No special permissions are required. Risk: installing new peer dependencies can conflict with an existing MUI version; check your package.json before running the command.
import React, { useState, useEffect } from 'react';
import { createTheme, useMediaQuery } from '@mui/material';
import Box from '@mui/material/Box';
import Sidebar from './Sidebar';
const theme = createTheme();
export default function ResponsiveLayout() {
// undefined on the server, true/false after client hydration
const isMdOrLarger = useMediaQuery(theme.breakpoints.up('md'));
// Guard against SSR hydration mismatch
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) {
return <Box>Loading layout...</Box>;
}
return (
<Box sx={{ display: 'flex' }}>
{isMdOrLarger && <Sidebar />}
<Box sx={{ flex: 1 }}>Main content</Box>
</Box>
);
}
theme.breakpoints.up('md') resolves to a media query such as @media (min-width:960px) in MUI's default theme. The hook returns undefined during server rendering because there is no window object. The mounted state prevents the component from rendering the sidebar conditionally until the client has measured the viewport.
Diagnostic decision: check before you ship
Open your browser's developer tools and resize the window across the md threshold. The sidebar should appear and disappear only at the breakpoint, not on every pixel change. In the React component tree, confirm that the hook's value changes once per breakpoint crossing. If you see warnings about hydration mismatches, the server and client initial renders differ.
For heavy components, frequent resize events can trigger extra re-renders. MUI debounces the resize handler, but you can still wrap the consuming component in React.memo or extract the hook into a custom hook used by a small presentational component so the rest of the tree does not re-render unnecessarily.
Limitations
- Server-side rendering:
useMediaQueryreturnsundefineduntil hydration. Always guard the first render or accept a brief fallback UI. - Orientation changes: the hook relies on the window resize event, so rapid rotations may show a slight lag before the boolean updates.
- Runtime cost: each hook instance adds a resize listener. Use it at the leaf level where possible rather than in a high-level wrapper that forces many siblings to re-render.
Practical check
- Render the example page on a desktop browser.
- Resize the viewport below the
mdbreakpoint. - Observe that the sidebar unmounts and the main content expands.
- Refresh the page and confirm no console errors related to hydration mismatch.
If the sidebar toggles at the expected width and the console stays clean, the hook is wired to your theme correctly. If it toggles too early or too late, review your theme breakpoint values; do not hardcode pixel widths in the component.
useMediaQuery is a good fit when your responsive logic already follows Material UI breakpoints. It is not a replacement for CSS media queries in every case; use it when the decision to render or hide a React component depends on screen size.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.