Skip to content
Corsair UI
Browse the docs

HooksNo. 107 of 109

useMediaQuery

Subscribes to a CSS media query. SSR-safe: renders a fallback on the server and hydrates without a mismatch.

Installation

pnpm dlx shadcn@latest add @corsair-ui/use-media-query

No setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.

Examples

Live queries

Resize the window or change your OS settings.

Viewport at least 768px wide(min-width: 768px)
no match
Reduced motion requested(prefers-reduced-motion: reduce)
no match
Touch-first pointer(pointer: coarse)
no match

API

PropTypeDefault
query

Any CSS media query.

string—
defaultValue

Returned on the server and during hydration.

booleanfalse

Compatibility

Tailwind CSS
3.4 and 4, both checked in CI
React
19
Rendering
Works in server components

Source

1 file, installed as source you own
use-media-query.tsView on GitHub
import { useCallback, useSyncExternalStore } from "react";

export interface UseMediaQueryOptions {
  /**
   * What to render before the browser can answer: on the server and during
   * hydration. Pick the value that matches your most common viewport to avoid
   * a visible swap after hydration.
   */
  defaultValue?: boolean;
}

/**
 * Tracks whether a CSS media query currently matches.
 *
 * Built on `useSyncExternalStore`, so hydration uses `defaultValue` and then
 * re-renders with the real match, instead of starting at `false` and flashing
 * the wrong layout the way a `useState` + `useEffect` version does.
 *
 * @example
 * const isDesktop = useMediaQuery("(min-width: 768px)");
 * const prefersReducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
 */
export function useMediaQuery(query: string, { defaultValue = false }: UseMediaQueryOptions = {}) {
  const subscribe = useCallback(
    (onChange: () => void) => {
      const mediaQueryList = window.matchMedia(query);
      mediaQueryList.addEventListener("change", onChange);
      return () => mediaQueryList.removeEventListener("change", onChange);
    },
    [query]
  );

  const getSnapshot = () => window.matchMedia(query).matches;
  const getServerSnapshot = () => defaultValue;

  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}