typescriptreacthooksresponsivemedia-query
useMediaQuery Hook
React hook for responsive design with media queries. SSR-safe with proper hydration handling and TypeScript support.
Code
import { useState, useEffect, useCallback } from 'react';
function useMediaQuery(query: string): boolean {
// Return false during SSR to avoid hydration mismatch
const getMatches = useCallback((): boolean => {
if (typeof window === 'undefined') return false;
return window.matchMedia(query).matches;
}, [query]);
const [matches, setMatches] = useState<boolean>(getMatches);
useEffect(() => {
if (typeof window === 'undefined') return;
const mediaQuery = window.matchMedia(query);
// Set initial value after mount (handles SSR hydration)
setMatches(mediaQuery.matches);
const handler = (event: MediaQueryListEvent) => {
setMatches(event.matches);
};
// Modern browsers
mediaQuery.addEventListener('change', handler);
return () => {
mediaQuery.removeEventListener('change', handler);
};
}, [query]);
return matches;
}
// Usage
const isMobile = useMediaQuery('(max-width: 768px)');
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
const isDesktop = useMediaQuery('(min-width: 1025px)');
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');Variations
Prefers color scheme hook
function usePrefersColorScheme(): 'light' | 'dark' | 'no-preference' {
const isDark = useMediaQuery('(prefers-color-scheme: dark)');
const isLight = useMediaQuery('(prefers-color-scheme: light)');
if (isDark) return 'dark';
if (isLight) return 'light';
return 'no-preference';
}
// Usage
function ThemeProvider({ children }: { children: React.ReactNode }) {
const colorScheme = usePrefersColorScheme();
const [theme, setTheme] = useState(colorScheme === 'dark' ? 'dark' : 'light');
// Sync with system preference unless user has explicitly chosen
const [userOverride, setUserOverride] = useState(false);
useEffect(() => {
if (!userOverride) {
setTheme(colorScheme === 'dark' ? 'dark' : 'light');
}
}, [colorScheme, userOverride]);
return <div data-theme={theme}>{children}</div>;
}