Skip to contentVibraUI
Foundation

useMediaQuery

Tracks whether a CSS media query currently matches, safely on the server and the client.

Built on useSyncExternalStore, so there is no render-then-correct flash on mount.

Install

npx shadcn@latest add @vibra/use-media-query

Needs the @vibra registry in your components.json — set it up once.

Examples

Props

PropTypeDefaultDescription
querystring—CSS media query to watch, e.g. "(min-width: 768px)".
returnsboolean—Whether the query currently matches; false on the server and before the first client read.

Dependencies

Registry

Source

hooks/use-media-query.ts
import * as React from "react"

/** The live MediaQueryList for `query`, or `undefined` when matchMedia isn't available (SSR, old browsers, tests). */
function getMediaQueryList(query: string): MediaQueryList | undefined {
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return undefined
  return window.matchMedia(query)
}

/** Tracks whether a CSS media query matches; false on the server and until matchMedia is read, then synced via the "change" event through useSyncExternalStore. */
export function useMediaQuery(query: string): boolean {
  const subscribe = React.useCallback(
    (onStoreChange: () => void) => {
      const mql = getMediaQueryList(query)
      if (!mql) return () => {}
      mql.addEventListener("change", onStoreChange)
      return () => mql.removeEventListener("change", onStoreChange)
    },
    [query]
  )
  const getSnapshot = React.useCallback(() => getMediaQueryList(query)?.matches ?? false, [query])
  const getServerSnapshot = () => false

  return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
}