DOM

useMediaQuery

Tracks whether a CSS media query currently matches.

Demo

(min-width: 768px)
false
(orientation: landscape)
false
(prefers-color-scheme: dark)
false
(prefers-reduced-motion: reduce)
false

Resize the window across 768px — the first row flips live as the query starts and stops matching.

Usage

use-media-query.tsxtsx
import { useMediaQuery } from "hookli";

export function Demo() {
  const isWide = useMediaQuery("(min-width: 768px)");

  return <p>{isWide ? "Desktop layout" : "Mobile layout"}</p>;
}

API

useMediaQuery.d.tsts
useMediaQuery(query: string, options?: UseMediaQueryOptions): boolean

Parameters

NameTypeDefaultDescription
querystringA CSS media query string, e.g. "(min-width: 768px)" or "(prefers-color-scheme: dark)".
optionsUseMediaQueryOptions{}SSR default and hydration behaviour. Optional.

Returns

NameTypeDescription
matchesbooleanWhether the query currently matches. Re-renders on every matchMedia change event, and starts from defaultValue on the server.

UseMediaQueryOptions

Configures the server value and mount behaviour.

NameTypeDescription
defaultValuebooleanValue returned on the server and before hydration.
initializeWithValuebooleanRead the real match synchronously on mount. Set false to always start from defaultValue and avoid a hydration mismatch.

Hook

The implementation, straight from hookli. Copy it, or install the package.

src/hooks/use-media-query/use-media-query.tstsx
import { useState } from "react";
import { useIsomorphicLayoutEffect } from "../use-isomorphic-layout-effect/use-isomorphic-layout-effect";

interface UseMediaQueryOptions {
  defaultValue?: boolean;
  initializeWithValue?: boolean;
}

const IS_SERVER = typeof window === "undefined";

export function useMediaQuery(
  query: string,
  options: UseMediaQueryOptions = {},
): boolean {
  const { defaultValue = false, initializeWithValue = true } = options;

  const getMatches = (mediaQuery: string): boolean => {
    if (IS_SERVER) return defaultValue;
    return window.matchMedia(mediaQuery).matches;
  };

  const [matches, setMatches] = useState<boolean>(() => {
    if (initializeWithValue) return getMatches(query);
    return defaultValue;
  });

  useIsomorphicLayoutEffect(() => {
    if (IS_SERVER) return;
    const matchMedia = window.matchMedia(query);
    const handleChange = () => setMatches(matchMedia.matches);
    handleChange();
    matchMedia.addEventListener("change", handleChange);
    return () => {
      matchMedia.removeEventListener("change", handleChange);
    };
  }, [query]);

  return matches;
}

View source on GitHub

Related hooks