Code this, not that! Don't use "window.onresize"!

There are better ways to check for breakpoints in JavaScript.
João Rosa

CSS media queries and breakpoints are amazing. They allow us to completely change the looks and organization of a wesbsite’s elements based on the size of the device being used. However, sometimes CSS is not enough, and we need to use JavaScript to run some specific logic at a given breakpoint. So probably all frontend developers have done something like this at some point in their life:

window.onresize = () => {
  if (window.innerWidth < 600) {
    // do mobile layout stuff
  } else {
    // do tablet/desktop layout stuff
  }
}
Using the "onresize" event to detect when the viewport has crossed a certain breakpoint

This might look pretty innocuous, but the issue is that such code will run every single time the viewport changes size, including when rotating the screen's orientation.

The snippet will run whenever the viewport's dimensions change even if by a single pixel, whether it's needed or not.

You might say "oh, but resizing barely ever happens!". But I would argue that just because something rarely happens doesn't mean you shouldn't prepare and optimise for when it does.

The fact is that we know we have limited resources in the browser, and every little bit of work piles up, so if we can avoid doing it and defer it to when such work is actually needed, we'll ultimately be improving the overall performance of our websites. This is especially true if functions like the one above do some heavy processing or checking or synchronous operations which might cause the layout to reflow, which end up contributing to poor perceived performance by our users.

Is there a better way of doing it?

Why, yes there is! Enter window.matchMedia(). This is a globally-available Window API that returns an object which can be used to determine if the document matches the media query string passed in as an argument. This match can be checked both immediately or via event.

const layoutChangedCallback = (matches) => {
  console.log(matches ? "I'm on desktop!" : "I'm on mobile!");
}
 
// set media query
const mql = window.matchMedia('(min-width: 600px)');

// set listener to run callback
mql.addEventListener('change', (e) => layoutChangedCallback(e.matches));

// the callback doesn't run immediately, so we run it manually once
layoutChangedCallback(mql.matches);
The snippet will run only on the first call and when the viewport actually crosses a breakpoint!

Just a small caveat...

If you need to support Safari < 14, you must use addListener() instead of addEventListener(). That function is now deprecated on all browsers but it still works, so consider it only if you must support older Safari versions.

In other words...

const mql = window.matchMedia('(min-width: 600px)');
 
// modern browsers
mql.addEventListener('change', (e) => layoutChangedCallback(e.matches));
 
// older Safari
mql.addListener((e) => layoutChangedCallback(e.matches));
 
layoutChangedCallback(mql.matches);
Using "matchMedia" on modern vs. older browsers

But that's not all!

matchMedia() is not just for detecting breakpoints. You can (and should) use it for anything you can detect using CSS media queries! This includes detecting for user preferences, like dark mode or prefers reduced motion.

// a helper function for streamlining the usage of matchMedia
const installMediaQueryWatcher = (mediaQuery, layoutChangedCallback) => {
  const mql = window.matchMedia(mediaQuery);
  mql.addListener(e => layoutChangedCallback(e.matches));
  layoutChangedCallback(mql.matches);
};
 
installMediaQueryWatcher(`(prefers-color-scheme: dark)`, (matches) =>
  console.log(`User wants ${matches ? 'dark' : 'light'} mode`)
);
 
installMediaQueryWatcher(`(prefers-reduced-motion: reduce)`, (matches) =>
  console.log(`User wants ${matches ? 'no ' : ''}animations`)
);
 
installMediaQueryWatcher(`(min-width: 600px)`, (matches) =>
  console.log(`We're in ${matches ? 'desktop': 'mobile'} mode`)
);
Testing helper function. The first results are the ones returned immediately, and the other ones are returned after simulating the respective changes in the browser's dev tools

So is there a real use case for window.onresize?

Sure there is, but I would argue that only for operations that do require you to know the size of the window at all times, like when you are manually handling the positioning of certain floating elements on a page, for instance. And even if you do, do consider if you should maybe throttle or debounce the calls to such code. And if you need to keep track of the size of something within a page, you're probably better off with ResizeObserver.

About João Rosa

João Rosa started working at Cocomore in October 2015. Whether someone needs help with CSS or JavaScript, in bugfixing or developing in PHP – as a backend and frontend developer he is the “handyman” of our IT team. He loves the working atmosphere at Cocomore, because everyone is always super nice and helpful. Even in times when work is more stressful, everyone still finds the spirit to crack a joke and have a good laugh. Three words that describe the young Portuguese best: versatile, obstinate, witty.