# Understanding the useMediaQuery Hook for Server-Side Rendering in Material-UI

> Learn how to use the MUI useMediaQuery hook for server-side rendering SSR safely. Prevent hydration errors and ensure responsive UIs with this essential guide.

- Repository: [MUI/material-ui](https://github.com/mui/material-ui)
- Tags: deep-dive
- Published: 2026-02-26

---

**The `useMediaQuery` hook in Material-UI enables responsive components by detecting CSS media query matches, using default values during server-side rendering to prevent hydration mismatches before switching to live browser evaluation on the client.**

The `useMediaQuery` hook is essential for building responsive React applications with Material-UI, allowing components to adapt their rendering based on viewport size, color scheme preferences, or other CSS media features. When implementing **server-side rendering (SSR)** with the mui/material-ui library, this hook requires special handling because the server environment lacks a `window` object to evaluate media queries. The implementation in [`packages/mui-material/src/useMediaQuery.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/useMediaQuery.ts) solves this through a sophisticated dual-mode architecture that ensures deterministic server output while maintaining full responsiveness after client hydration.

## How useMediaQuery Handles Server-Side Rendering

During SSR, React components execute in a Node.js environment where `window.matchMedia` does not exist. The hook addresses this constraint through lazy evaluation and theme integration.

### Lazy Evaluation with Default Match Values

When `useMediaQuery` executes on the server, it cannot access real media information, so it returns a **default match value** to ensure the initial HTML is deterministic. This prevents the React hydration error that occurs when server and client renders produce different markup.

The hook accepts a `defaultMatches` option that explicitly sets the server-side value:

```tsx
import useMediaQuery from '@mui/material/useMediaQuery';

function DarkModeToggle() {
  // Assume dark mode on the server; client will correct if needed
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)', {
    defaultMatches: true,
  });

  return (
    <button>{prefersDark ? 'Dark Mode' : 'Light Mode'}</button>
  );
}

```

If `defaultMatches` is not provided, the hook attempts to compute a sensible default based on the Material-UI theme configuration.

### ThemeProvider Integration for Breakpoint Defaults

Material-UI's `ThemeProvider` supplies a theme object containing **breakpoints** that describe the application's responsive design system. The `useMediaQuery` hook reads this theme via `useTheme` from [`packages/mui-material/src/useTheme.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/useTheme.ts) and uses the breakpoints to infer default match values when no explicit `defaultMatches` is provided.

This integration ensures that server-rendered HTML aligns with the design system's default assumptions, typically rendering the mobile breakpoint on the server unless configured otherwise:

```tsx
import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';

function ResponsiveComponent() {
  const theme = useTheme();
  // Uses theme breakpoints to determine default on server
  const isDesktop = useMediaQuery(theme.breakpoints.up('md'));

  return isDesktop ? <DesktopLayout /> : <MobileLayout />;
}

```

The breakpoint helpers are defined in [`packages/mui-material/src/styles/createBreakpoints.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/styles/createBreakpoints.ts), providing the `up`, `down`, `between`, and `only` methods that generate the media query strings.

## Client-Side Hydration and Media Query Listeners

Once the React application hydrates on the client, `useMediaQuery` transitions from static default values to live browser evaluation.

### Re-evaluation After Mount

After the component mounts, the hook creates a `MediaQueryList` instance using `window.matchMedia(query)` and registers an event listener for changes. When the media query result changes (e.g., the user resizes the browser window), the hook updates its internal state, triggering a re-render with the correct responsive value.

This mechanism is implemented in the `useEffect` hook within [`packages/mui-material/src/useMediaQuery.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/useMediaQuery.ts):

```tsx
// Simplified conceptual implementation
useEffect(() => {
  const mediaQueryList = window.matchMedia(query);
  
  const listener = (event) => {
    setMatches(event.matches);
  };
  
  mediaQueryList.addEventListener('change', listener);
  return () => mediaQueryList.removeEventListener('change', listener);
}, [query]);

```

### The noSsr Option for Controlled Hydration

For scenarios where developers want to skip the server-side default entirely and wait for the client to evaluate the media query, `useMediaQuery` accepts a `noSsr` option. When set to `true`, the hook returns `undefined` (or a specified default) during the server render and initial client render, only providing the actual match value after hydration completes.

This is useful for components that should not render any content until the true viewport size is known:

```tsx
import useMediaQuery from '@mui/material/useMediaQuery';

function PrintOnly() {
  // Returns undefined on server, true/false only after client mount
  const isPrint = useMediaQuery('print', { noSsr: true });

  if (isPrint === undefined) return null;
  
  return isPrint ? <PrintBanner /> : null;
}

```

## Performance Optimizations in useMediaQuery

The implementation in [`packages/mui-material/src/useMediaQuery.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/useMediaQuery.ts) includes several optimizations to prevent unnecessary re-renders and listener registrations.

The hook uses `React.useMemo` to memoize the media query string and options object, ensuring that the `MediaQueryList` is only recreated when the query actually changes. Additionally, the hook maintains a stable listener reference to avoid repeatedly adding and removing event listeners during component updates.

These optimizations are crucial when `useMediaQuery` is used in multiple components throughout an application, as they prevent the performance degradation that would occur if each hook instance created fresh media query listeners on every render.

## Practical Implementation Examples

The following examples demonstrate common patterns for using `useMediaQuery` with server-side rendering in Material-UI applications.

### Responsive Layout with Theme Breakpoints

```tsx
import * as React from 'react';
import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';
import Box from '@mui/material/Box';

export default function ResponsiveLayout() {
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
  const isDesktop = useMediaQuery(theme.breakpoints.up('md'));

  return (
    <Box>
      {isMobile && <MobileNavigation />}
      {isDesktop && <DesktopSidebar />}
      <MainContent />
    </Box>
  );
}

```

### Dark Mode Detection with SSR Default

```tsx
import useMediaQuery from '@mui/material/useMediaQuery';

function App() {
  // Server renders assuming light mode, client corrects if user prefers dark
  const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)', {
    defaultMatches: false,
  });

  return (
    <ThemeProvider theme={prefersDarkMode ? darkTheme : lightTheme}>
      <Dashboard />
    </ThemeProvider>
  );
}

```

### Print Media Query with No SSR

```tsx
import useMediaQuery from '@mui/material/useMediaQuery';

function InvoicePage() {
  // Only evaluate on client, return null during SSR
  const isPrinting = useMediaQuery('print', { noSsr: true });
  
  if (isPrinting === undefined) {
    return <StandardView />;
  }

  return isPrinting ? <PrintOptimizedInvoice /> : <StandardView />;
}

```

## Summary

- **`useMediaQuery`** enables responsive React components by evaluating CSS media queries, with special handling for server-side rendering environments where `window` is unavailable.
- **Server-side behavior** relies on `defaultMatches` or theme breakpoint inference to return deterministic values during the initial render, preventing hydration mismatches.
- **Client-side hydration** transitions the hook to live evaluation using `window.matchMedia`, with automatic state updates when viewport conditions change.
- **Performance optimizations** include memoization of query strings and stable event listener management to prevent unnecessary re-renders.
- **Configuration options** like `noSsr` provide fine-grained control over when and how media queries are evaluated during the server-client transition.

## Frequently Asked Questions

### How does useMediaQuery prevent hydration mismatches during server-side rendering?

The hook prevents hydration mismatches by returning a **deterministic default value** during the server render. When running on the server, `useMediaQuery` cannot access `window.matchMedia`, so it either uses the `defaultMatches` option provided by the developer or infers a value from the theme's breakpoints. This ensures the HTML generated on the server matches the initial client render, allowing React to hydrate without errors. Once the component mounts on the client, the hook switches to the actual browser evaluation.

### What is the difference between defaultMatches and noSsr options?

The `defaultMatches` option specifies the boolean value the hook should return during server-side rendering when the actual media query cannot be evaluated, ensuring deterministic output. In contrast, the `noSsr` option controls whether the hook should render anything at all during SSR. When `noSsr` is set to `true`, the hook returns `undefined` (or the default value) during both server and initial client renders, waiting until after hydration to evaluate the media query. Use `defaultMatches` to make assumptions about the viewport, and `noSsr` when you want to defer rendering entirely until the client environment is available.

### How does useMediaQuery integrate with Material-UI theme breakpoints?

The hook integrates with the theme via the `useTheme` hook from [`packages/mui-material/src/useTheme.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/useTheme.ts). When you pass a breakpoint query like `theme.breakpoints.up('md')`, the hook receives a media query string generated by the breakpoint system defined in [`packages/mui-material/src/styles/createBreakpoints.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/styles/createBreakpoints.ts). If no `defaultMatches` option is provided, `useMediaQuery` examines the theme's breakpoints to infer a sensible default value for server-side rendering, typically assuming the smallest breakpoint. This tight integration ensures that responsive layouts remain consistent with the design system during both server and client execution.

### Can useMediaQuery be used outside of Material-UI's ThemeProvider?

Yes, `useMediaQuery` can function independently of `ThemeProvider`, but with limitations. Without a theme context, the hook cannot infer default values from breakpoints during server-side rendering, so you must explicitly provide the `defaultMatches` option to avoid hydration mismatches. The hook will still create `MediaQueryList` listeners on the client and respond to viewport changes correctly. However, for consistent SSR behavior and access to breakpoint helpers, wrapping your application in `ThemeProvider` is recommended.