'use client'
import { useCallback, useSyncExternalStore } from 'react'
export interface UseMediaQueryOptions {
/**
* Value returned on the server and during the first client render before
* hydration, when `window.matchMedia` is unavailable. Defaults to `false`.
*/
defaultValue?: boolean
}
/**
* SSR-safe `matchMedia` hook. Returns whether `query` currently matches and
* re-renders when it changes. Works in any React environment (Vite, CRA,
* Next.js, Remix, Astro): on the server and the first client render it returns
* `defaultValue`, then the real value resolves on hydration via
* `useSyncExternalStore`.
*
* @example
* const isDesktop = useMediaQuery('(min-width: 768px)')
* return isDesktop ? :
*/
export function useMediaQuery(query: string, { defaultValue = false }: UseMediaQueryOptions = {}): boolean {
const subscribe = useCallback(
(onChange: () => void) => {
const mq = window.matchMedia(query)
mq.addEventListener('change', onChange)
return () => mq.removeEventListener('change', onChange)
},
[query],
)
const getSnapshot = useCallback(() => window.matchMedia(query).matches, [query])
const getServerSnapshot = useCallback(() => defaultValue, [defaultValue])
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
}