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>
);
}