-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathuse-media-query.ts
More file actions
48 lines (39 loc) · 1.38 KB
/
Copy pathuse-media-query.ts
File metadata and controls
48 lines (39 loc) · 1.38 KB
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
"use client";
import { useEffect, useState, useSyncExternalStore } from "react";
export function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
// Set initial value
setMatches(media.matches);
// Create listener
const listener = (event: MediaQueryListEvent) => {
setMatches(event.matches);
};
// Add listener
media.addEventListener("change", listener);
// Cleanup
return () => media.removeEventListener("change", listener);
}, [query]);
return matches;
}
/**
* Returns `true` on mobile viewports (<=767px).
*
* Uses `useSyncExternalStore` with a server snapshot of `false` so that the
* first client render matches the server (no hydration mismatch), and then
* immediately syncs to the real value on the client. Components that need to
* avoid a layout flash should hide mobile-only UI with CSS (`lg:hidden`)
* rather than conditionally rendering based on this hook.
*/
export function useIsMobile(): boolean {
return useSyncExternalStore(
(callback) => {
const mql = window.matchMedia("(max-width: 767px)");
mql.addEventListener("change", callback);
return () => mql.removeEventListener("change", callback);
},
() => window.matchMedia("(max-width: 767px)").matches,
() => false, // server snapshot
);
}