All files / web/src/hooks useMediaQuery.ts

100% Statements 56/56
100% Branches 9/9
100% Functions 5/5
100% Lines 56/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 571x 1x 1x 1x 1x 1x 1x 1x 1x 1x 25x 25x 25x 25x 25x 7x 25x 25x 25x 14x 7x 7x 7x 7x 7x 7x 7x 7x 1x 1x 7x 7x 7x 7x 7x 7x 7x 25x 25x 25x 25x 1x 1x 1x 1x 1x 3x 3x 1x 1x 3x 3x 1x 1x 3x 3x  
'use client'
 
import { useEffect, useState } from 'react'
 
/**
 * Hook to detect media query matches
 * @param query - CSS media query string (e.g., '(min-width: 768px)')
 * @returns boolean indicating if the query matches
 */
export function useMediaQuery(query: string): boolean {
  // Initialize with false to avoid hydration mismatch
  const [matches, setMatches] = useState(false)
  const [isClient, setIsClient] = useState(false)
 
  useEffect(() => {
    setIsClient(true)
  }, [])
 
  useEffect(() => {
    if (!isClient) return
 
    const mediaQuery = window.matchMedia(query)
 
    // Set initial value
    setMatches(mediaQuery.matches)
 
    // Create event listener
    const handler = (event: MediaQueryListEvent) => {
      setMatches(event.matches)
    }
 
    // Modern browsers
    mediaQuery.addEventListener('change', handler)
 
    return () => {
      mediaQuery.removeEventListener('change', handler)
    }
  }, [query, isClient])
 
  return matches
}
 
/**
 * Common breakpoint hooks
 */
export function useIsMobile(): boolean {
  return useMediaQuery('(max-width: 767px)')
}
 
export function useIsTablet(): boolean {
  return useMediaQuery('(min-width: 768px) and (max-width: 1023px)')
}
 
export function useIsDesktop(): boolean {
  return useMediaQuery('(min-width: 1024px)')
}