useMediaQuery

Layout/Viewport

A React hook that tracks the state of CSS media queries, enabling responsive behavior and feature detection.

Demo

Try resizing your window, changing your system theme, or rotating your device to see the values update:

Screen Size
Large Screen (≥1024px):No
Color Scheme
Dark Mode:No
Motion Preference
Reduced Motion:No
Orientation
Portrait Mode:No
Note: Some values may require system-level changes to update.

Installation

npm install @thibault.sh/hooks

Usage

import { useMediaQuery } from '@thibault.sh/hooks/useMediaQuery';

function ResponsiveComponent() {
  const isMobile = useMediaQuery('(max-width: 768px)');
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');

  return (
    <div>
      <p>Current device: {isMobile ? 'Mobile' : 'Desktop'}</p>
      <p>Theme preference: {prefersDark ? 'Dark' : 'Light'}</p>
    </div>
  );
}

API

Returns

boolean indicating if the media query matches

Features

  • Real-time Updates

    Automatically updates when media query state changes

  • SSR Compatible

    Safely handles server-side rendering scenarios

  • Standard Compliant

    Uses standard CSS media query syntax for compatibility

  • Memory Efficient

    Reuses media query listeners for identical queries across components

Responsive Layout Example

import { useMediaQuery } from '@thibault.sh/hooks/useMediaQuery';

function ResponsiveLayout({ children }: { children: React.ReactNode }) {
  const isWideScreen = useMediaQuery('(min-width: 1200px)');
  const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1199px)');
  const isMobile = useMediaQuery('(max-width: 767px)');

  return (
    <div className={
      isWideScreen
        ? 'grid grid-cols-3 gap-4'
        : isTablet
        ? 'grid grid-cols-2 gap-3'
        : 'flex flex-col gap-2'
    }>
      {children}
    </div>
  );
}

Feature Detection Example

import { useMediaQuery } from '@thibault.sh/hooks/useMediaQuery';

function AccessibleAnimation() {
  const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
  const supportsHover = useMediaQuery('(hover: hover)');

  return (
    <button
      className={`
        transition-transform
        ${!prefersReducedMotion && 'hover:scale-105'}
        ${supportsHover ? 'hover:bg-blue-600' : 'active:bg-blue-600'}
      `}
    >
      Interactive Button
    </button>
  );
}

Theme Switcher Example

import { useMediaQuery } from '@thibault.sh/hooks/useMediaQuery';
import { useEffect } from 'react';

function ThemeSwitcher() {
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
  const prefersLight = useMediaQuery('(prefers-color-scheme: light)');
  
  useEffect(() => {
    // Update theme based on system preference
    document.documentElement.classList.toggle('dark', prefersDark);
    document.documentElement.classList.toggle('light', prefersLight);
  }, [prefersDark, prefersLight]);

  return (
    <div className="text-sm text-muted-foreground">
      Current theme: {prefersDark ? 'Dark' : prefersLight ? 'Light' : 'No Preference'}
    </div>
  );
}