DEV Community

Cover image for Build a custom media query composable for Vue apps
Mayowa Ojo
Mayowa Ojo

Posted on

Build a custom media query composable for Vue apps

Introduction

Let's face it, building a fully responsive website can be a daunting task and while the functionality CSS provides are usually enough to make your site adapt to the various screen sizes, there are times when you need a little JavaScript to get it just right and nail that smooth user experience on every device.

Imagine for a second, you're building a dashboard for some e-commerce store to manage all sorts of data and you have a sidebar for navigating the various menus. Naturally the sidebar is the less important component of the page so when you want to adjust for smaller screen sizes, this is the component that gives way. We can hide the sidebar completely on mobile and even show a different variant of the sidebar on tablets.

JavaScript media queries offer the flexibility to conditionally render components which brings performance benefits because you don't have to render elements that are not visible to the user.

The Composable

Composables in Vue 3 are like hooks in react. They provide an intuitive way to extract reactive state and functionality to separate modules or as you will, composables.

Note: The code here leverages features unique to Vue 3 as it makes use of the composition API.

We're going to build a media query composable that accepts a media query string and essentially listens for changes in the window size and returns a variable that tells us if the current window size matches the query string.

// useMedia.js
import { ref, watchEffect } from "vue";

export const useMedia = (query) => {
   const matches = ref(true);

   watchEffect((onInvalidate) => {
      const media = window.matchMedia(query);

      if(media.matches !== matches) {
         matches.value = media.matches;
      }

      const onChange = () => {
         matches.value = media.matches;
      }

      media.addEventListener("change", onChange);

      onInvalidate(() => {
         media.removeEventListener("change", onChange);
      });
   });

   return matches
}
Enter fullscreen mode Exit fullscreen mode

Usage

import { useMedia } from "../../composables/useMedia";
...
const isMobile = useMedia("(max-width: 425px)")
...
Enter fullscreen mode Exit fullscreen mode

In the snippet above we use the watchEffect function to subscribe to changes in the matches variable. In the watchEffect callback, we are listening for a change event on the matchMedia property of the window object. When a change is detected, it calls the onChange function which will update the matches reactive object.

We also have an onInvalidate function being called, which receives a callback function where we remove the event listener. This is essentially a clean-up function like you'd have in a react useEffect hook. This lets us cancel all subscriptions when the component is unmounted.

This is how we can easily build our own custom re-usable function to use JavaScript media queries in our components. If you have any questions or thoughts on this, drop a comment below.

References

JavaScript Media Queries
Composables
Side Effect Invalidation

Discussion (0)