typescriptbeginner

React Responsive Breakpoint Hook

A useMediaQuery hook for responsive rendering with SSR-safe breakpoint detection.

typescript
'use client';
import { useState, useEffect } from 'react';

function useMediaQuery(query: string): boolean {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const media = window.matchMedia(query);
    setMatches(media.matches);

    const listener = (e: MediaQueryListEvent) => setMatches(e.matches);
    media.addEventListener('change', listener);
    return () => media.removeEventListener('change', listener);
  }, [query]);

  return matches;
}

// Predefined breakpoints (Tailwind defaults)
function useBreakpoint() {
  const isSm = useMediaQuery('(min-width: 640px)');
  const isMd = useMediaQuery('(min-width: 768px)');
  const isLg = useMediaQuery('(min-width: 1024px)');
  const isXl = useMediaQuery('(min-width: 1280px)');
  const is2xl = useMediaQuery('(min-width: 1536px)');

  return { isSm, isMd, isLg, isXl, is2xl };
}

// Usage
function ResponsiveLayout() {
  const { isMd, isLg } = useBreakpoint();
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');

  const columns = isLg ? 3 : isMd ? 2 : 1;

  return (
    <div>
      <p>Columns: {columns}</p>
      <p>Theme: {prefersDark ? 'dark' : 'light'}</p>
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: 16 }}>
        {Array.from({ length: 6 }, (_, i) => (
          <div key={i} className="p-4 bg-gray-800 rounded">
            Card {i + 1}
          </div>
        ))}
      </div>
      {!isMd && (
        <nav className="fixed bottom-0 left-0 right-0 bg-black p-4">
          Mobile navigation
        </nav>
      )}
    </div>
  );
}

Use Cases

  • Conditional rendering based on screen size
  • Responsive grid layouts without CSS-only solutions
  • Showing mobile navigation on small screens

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.