Media queries in CSS are often used to change the styling or visibility of
elements when the website is accessed with a particular device/environment
or when the browser's viewport size or orientation changes. They are a
vital part of any responsive web design.
You may sometimes want to execute JavaScript code when a media query
condition has been met. For example, maybe you want to run code when the
viewport width is greater or equal to 1000px. The resize event is often
used in this situation:
There are multiple problems with this approach, however:
It can be expensive. The event listener callback will be called each
time the browser's window is resized. As a result, it can fire at an
extremely high rate and make your website feel sluggish if your callback
has expensive operations.
It can become complex. To mitigate performance issues and reduce the
frequency that the code inside the event listener callback executes, a
developer may use debouncing or throttling. In addition, if you only want
the code in the callback to run when it crosses the breakpoint, you
may have to introduce some state that tracks if the viewport has already
crossed a particular breakpoint so that it doesn't fire multiple times.
There must be a better way to execute some JavaScript code when the
viewport crosses a particular breakpoint or meets a media query condition.
Luckily, JavaScript has your back!
Using matchMedia() in JavaScript
The matchMedia API is a performant and simple solution that should be
considered when you want JavaScript code to execute when a media query
status has changed.
Unlike the resize event handler callback, the callback passed to
mediaQuery.addEventListener() in the example above will only fire when a
change in media query status has occurred. Because of this, we don't
have to worry about using throttling or debouncing or keeping track of
whether the code has already been executed for a given viewport state.
Using addListener to support older browsers.
There is a caveat with this approach, though. Older browsers such as
versions of Safari before version 14 implement an
older and now deprecated API.
They don't support mediaQuery.addEventListener() and will throw an error
if used. Instead, they use mediaQuery.addListener() which has a slightly
different call signature.
Because of the differences in browser support, it is imperative to use
feature detection
so that the widest range of browsers are supported:
Running code on page load
The matchMedia API also supports querying the media query status. This
can be important when setting up the page's initial state during page load,
for example:
Full solution
The full native JavaScript solution will often look something like the
following:
Using media queries in React
The matchMedia API can also be a useful hook in React. Here it is with
typescript:
Using the hook is then as simple as passing in the media query:
Conclusion
JavaScript's matchMedia API is a simple and performant way of detecting
when a media query status change has occurred. Use it without the
performance penalty of alternative solutions like listening to the resize
event.