In these modern times, your web applications can be viewed in a variety of screen sizes -- from small screen phones to large 4k monitors. Luckily CSS allows us to add certain stylings depending on many variables using media queries. Sometimes using media queries isn't enough to achieve the goal. This is where matchMedia could help.
matchMedia is a method provided by window that can determine whether the given media query matches the current state of the browser.
matchMedia
matchMedia accepts a media query as a string and returns a MediaQueryList which can be used to check if the current state of the browser matches the given media query.
const mediaQueryList = window.matchMedia("only screen and (max-width: 600px)");
if (mediaQueryList.matches) {
console.log("Matches");
} else {
console.log("Does not match");
}
Keeping track of changes
We can keep track of these changes by listening for a change event.
const callback = (event) => {
if (event.matches) {
console.log("Matches");
} else {
console.log("Does not match");
}
}
mediaQueryList.addEventListener("change", callback);
mediaQueryList.removeEventListener("change", callback);
If you need to support older browsers, you can use addListener and removeListener respectively but do remember that those methods are deprecated.
mediaQueryList.addListener(callback);
mediaQueryList.removeListener(callback);
useMediaQuery
This technology can also be transferred to a reusable React hook. The hook will accept a media query and a callback function for when changes occur.
const useMediaQuery = (query, callback) => {
const [isMatchingQuery, setIsMatchingQuery] = useState(false);
useEffect(() => {
const mediaQueryList = window.matchMedia(query);
const onMediaQueryUpdate = (e) => {
setIsMatching(e.matches);
if(callback) {
callback(e);
}
};
// Set whether the browser initially matches the query
setIsMatchingQuery(mediaQueryList.matches);
mediaQueryList.addEventListener("change", onMediaQueryUpdate);
return () => {
mediaQueryList.removeEventListener("change", onMediaQueryUpdate);
}
}, [query, callback, setIsMatchingQuery]);
return { isMatchingQuery };
}
If you're already using matchMedia in your project, how are you using it? If you're using a different framework, how would you incorporate matchMedia into that framework?
Top comments (3)
I currently use it for a bunch of things, with automatically detecting user theme preference being my most common and handy use.
With Vue, it's as simple as making the callback modify a reactive variable.
You can also easily make a "composable", which is very similar to your React example.
Based partly on the VueUse library's one:
useMediaQuery.ts
AwesomeComponent.vue
You do not really need to allow a callback to be passed in, as you can simply watch the returned
ref.Maybe the timing of reactivity updates would make it necessary, so that the callback gets called immediately when the media query changes, but I think that would be an extreme edge case.
useEventListeneris also a common composable.useEventListener.ts
Using this, we can rewrite useMediaQuery.
useMediaQuery.ts
Note: this will no longer work with browsers that only support
addListenerandremoveListenerand notaddEventListenerandremoveEventListener.We could also make this optionally take a ref to a string instead of just a string, and update the media query when it is changed, like what happens with
useEventListener. Or we could simplifyuseEventListener:).But for
useEventListener, having a reactive target makes sense, as this is useful in many cases, whileuseMediaQuerydoesn't really need that functionality.I haven't used
matchMediayet but it's a good tool to have in my arsenal of JS tricks. It can be used in my accessibility efforts to conform to WCAG.Very useful. Thank you