# How to Use the useMediaQuery Hook in Astryx for Responsive Breakpoints

> Learn to use the Astryx useMediaQuery hook for responsive breakpoints in React. Get automatic updates for viewport changes and ensure hydration-safe SSR with serverDefault.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: how-to-guide
- Published: 2026-07-02

---

**The `useMediaQuery` hook in Astryx enables React components to subscribe to CSS media queries and receive boolean values that update automatically when viewport conditions change, while providing a `serverDefault` parameter to ensure hydration-safe server-side rendering.**

The `useMediaQuery` hook is a core utility exported from the `@astryxdesign/core` package that bridges the gap between CSS media queries and React state. Located in the `facebook/astryx` repository, this 53-line implementation leverages React 18's `useSyncExternalStore` pattern to safely integrate `window.matchMedia` with both client and server environments.

## Core Implementation and SSR Safety

According to the Astryx source code in [`packages/core/src/hooks/useMediaQuery.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useMediaQuery.ts), the hook implements a three-phase subscription model that ensures consistency across SSR and client-side hydration.

**Subscription phase**: When the component mounts, the hook creates a `matchMedia` object for the supplied query and registers a `change` event listener using `mql.addEventListener('change', onStoreChange)`. The cleanup function removes this listener when the component unmounts.

**Snapshot phase**: On every render, the hook reads the current match state via `window.matchMedia(query).matches` to determine if the query currently matches.

**Server snapshot phase**: During server-side rendering where `window` is undefined, the hook returns the developer-provided `serverDefault` value instead of accessing the DOM, preventing hydration mismatches and layout shift on initial paint.

```tsx
// packages/core/src/hooks/useMediaQuery.ts
export function useMediaQuery(query: string, serverDefault = false): boolean {
  const subscribe = useCallback(
    (onStoreChange) => {
      const mql = window.matchMedia(query);
      mql.addEventListener('change', onStoreChange);
      return () => mql.removeEventListener('change', onStoreChange);
    },
    [query],
  );

  const getSnapshot = useCallback(() => window.matchMedia(query).matches, [query]);
  const getServerSnapshot = useCallback(() => serverDefault, [serverDefault]);

  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}

```

## Basic Breakpoint Detection

Use the `useMediaQuery` hook in Astryx to conditionally render content based on viewport width by passing standard CSS media query strings.

```tsx
import { useMediaQuery } from '@astryxdesign/core';

export function MobileOnly() {
  const isMobile = useMediaQuery('(max-width: 768px)');

  if (!isMobile) return null;
  return <div>This content is visible only on mobile devices.</div>;
}

```

The hook returns `true` when the viewport width is less than or equal to 768 pixels, and automatically re-renders the component when the user resizes the browser window or rotates their device.

## Configuring SSR Fallbacks

When using the `useMediaQuery` hook in Astryx within server-side rendered applications like Next.js, always provide the `serverDefault` parameter to avoid flashes of incorrect layout during hydration.

```tsx
// In a Next.js page component
export default function Page({ isMobileHint }: { isMobileHint: boolean }) {
  const isMobile = useMediaQuery('(max-width: 768px)', isMobileHint);

  return <div>{isMobile ? 'Mobile view' : 'Desktop view'}</div>;
}

// Provide the hint via getServerSideProps or similar
export async function getServerSideProps(context) {
  const ua = context.req.headers['user-agent'] || '';
  const isMobileHint = /Mobi|Android/i.test(ua);
  return { props: { isMobileHint } };
}

```

This pattern ensures the initial HTML rendered on the server matches the expected mobile or desktop layout, eliminating content-shift flicker that occurs when the client re-evaluates the query after hydration.

## Combining Multiple Breakpoints

For complex responsive layouts, compose multiple `useMediaQuery` calls to handle tablet, desktop, and mobile breakpoints simultaneously.

```tsx
export function ResponsiveLayout() {
  const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
  const isDesktop = useMediaQuery('(min-width: 1025px)');

  if (isTablet) return <TabletLayout />;
  if (isDesktop) return <DesktopLayout />;
  return <MobileLayout />;
}

```

Each hook operates independently and only triggers re-renders when its specific query boundary is crossed, ensuring optimal performance even when monitoring multiple viewport ranges.

## Detecting Interaction Capabilities

Beyond viewport width, use the `useMediaQuery` hook in Astryx to detect device capabilities such as hover support, which powers accessible styling decisions.

```tsx
export function HoverAwareButton() {
  const canHover = useMediaQuery('(hover: hover)');

  return (
    <button
      style={{
        cursor: canHover ? 'pointer' : 'default',
        backgroundColor: canHover ? '#0064E0' : '#ccc',
      }}
    >
      Click me
    </button>
  );
}

```

This same pattern is implemented in [`packages/core/src/theme/useTheme.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/theme/useTheme.ts) to detect system color scheme preferences via `(prefers-color-scheme: dark)`, and in [`packages/core/src/hooks/useMenuHover.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useMenuHover.ts) to conditionally enable hover-based interactions.

## Integration with Astryx Theme System

The `useMediaQuery` hook serves as the foundation for higher-level Astryx utilities. In [`packages/core/src/theme/useTheme.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/theme/useTheme.ts), the hook monitors `(prefers-color-scheme: dark)` to automatically switch between light and dark modes. Similarly, [`packages/core/src/hooks/useMenuHover.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useMenuHover.ts) uses the hook to detect fine pointer capabilities before attaching expensive mouse event listeners.

The hook is publicly exported from [`packages/core/src/hooks/index.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/index.ts), making it available via `import { useMediaQuery } from '@astryxdesign/core'`.

## Summary

- **The `useMediaQuery` hook in Astryx** wraps `window.matchMedia` in a React 18 `useSyncExternalStore` pattern to provide reactive boolean values for CSS media queries.
- **Always provide a `serverDefault`** when rendering on the server to prevent hydration mismatches and layout shift.
- **The hook accepts any valid CSS media query string**, enabling detection of viewport size, color scheme, hover capabilities, and other media features.
- **Source files to reference**: Implementation lives in [`packages/core/src/hooks/useMediaQuery.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useMediaQuery.ts), with consumption examples in [`packages/core/src/theme/useTheme.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/theme/useTheme.ts) and [`packages/core/src/hooks/useMenuHover.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useMenuHover.ts).

## Frequently Asked Questions

### How does useMediaQuery handle server-side rendering?

The `useMediaQuery` hook accepts a `serverDefault` parameter that defaults to `false`. During SSR, when `window` is undefined, the hook returns this default value instead of attempting to access `matchMedia`. After hydration, React replaces the server value with the actual client-side media query result, and the component re-renders if they differ.

### What is the performance impact of using multiple useMediaQuery hooks?

Each `useMediaQuery` instance creates its own `matchMedia` listener, but the browser optimizes multiple listeners for the same query. For distinct queries (like separate breakpoints for mobile, tablet, and desktop), the hook is efficient because it uses `useSyncExternalStore`, which prevents unnecessary re-renders through React's selective hydration and snapshot mechanisms.

### Can I use useMediaQuery for features other than viewport width?

Yes, the `useMediaQuery` hook in Astryx accepts any valid CSS media query string. Common use cases include detecting `(prefers-color-scheme: dark)` for theme switching, `(hover: hover)` for input capability detection, `(orientation: landscape)` for rotation handling, and `prefers-reduced-motion` for accessibility preferences.

### Where is the hook exported from in the Astryx package?

The `useMediaQuery` function is defined in [`packages/core/src/hooks/useMediaQuery.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/useMediaQuery.ts) and re-exported from [`packages/core/src/hooks/index.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/hooks/index.ts) as part of the public API. You can import it using `import { useMediaQuery } from '@astryxdesign/core'`.