ReactJS useMediaQuery hook using window.matchMedia(‘…’).

Pablo Garcia
2 min readMar 30, 2022

--

It is considered a bad practice to listen to the resize event with JavaScript because it can lead to many UX issues, and it doesn’t consider more complex scenarios. Now with the window.matchMedia API we can safely use media queries just like in CSS.

This is useful when in React (and other frameworks/libraries) you are forced to pass properties to a component that uses these to render it in a certain way. This makes it impossible to change the component with CSS only.

We can use the following React hook to listen to media queries and trigger a re-render to update the UI.

import { useEffect, useMemo, useState } from 'react'/**
* Check if a media query matches the UI
* @param {String} mediaQueryString
* @returns {Boolean}
*
* https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
* https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/change_event
*
* Example:
* useMediaQuery('(max-width: 600px)');
* useMediaQuery('only screen and (min-width: 600px)');
* useMediaQuery('@media only screen and (min-width: 600px)');
*/
export function useMediaQuery(mediaQueryString) {
const queryString = removeReservedMediaKeyWord(mediaQueryString)
const query = useMemo(() => window.matchMedia(queryString), [queryString])
const [matches, setMatches] = useState(query.matches) // one-time, instantaneous check
useEffect(() => {
const listener = (e) => setMatches(e.matches)
query.addEventListener('change', listener)
return () => query.removeEventListener('change', listener)
}, [query])
return matches
}
function removeReservedMediaKeyWord(mediaQueryString) {
return mediaQueryString.replace('@media', '').trim()
}

Polyfill

You should install a polyfill just in case: https://www.npmjs.com/package/matchmedia-polyfill.

Testing in Jest

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});

See: https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom

Related

Might also interest you; a list of media queries that I found useful with CSS in JS and are based on Bootstrap V5 breakpoint sizes:

https://pgarciacamou.medium.com/media-queries-with-css-in-jss-using-emotion-react-70bdb49d45c8

--

--

Pablo Garcia

Senior Engineer at Netflix, ex-Staff Architect 2 at PayPal. M.S. in Computer Science w/specialization in Computing Systems. B.Eng. in Computer Software.