Felix Astner
JavaScript, Magento and other Software
Felix Astner

Building a Media Query Composable in Vue

Building a Media Query Composable in Vue

Introduction

In modern web development, responsive design is crucial. Using media queries effectively allows your application to adapt to different screen sizes. This article demonstrates how to create a reusable media query composable in Vue that integrates seamlessly with Tailwind CSS theme configurations.

The Media Query Composable

Code Implementation

Here is the implementation of the media query composable:

import {ref, onMounted, onUnmounted, watch, onBeforeMount, type Ref} from 'vue';

const screens = {
    tablet: '640px',
    laptop: '1024px',
    desktop: '1280px'
}

type QueryType = 'up' | 'down';
type ScreenSize = keyof typeof screens;

interface MediaQuery {
    type: QueryType;
    size: ScreenSize;
}

/**
 * Determines if the current screen size matches the specified media query.
 *
 * @param {ScreenSize|MediaQuery} query - The screen size or media query to match.
 * @returns {Ref<boolean>} - A ref representing whether the media query matches.
 * @throws {Error} - If the query format is invalid or the screen size is not defined in `screens`.
 */
const useMediaQuery = (query: ScreenSize | MediaQuery): Ref<boolean> => {
    const matches = ref(false);
    let mediaQuery: MediaQueryList;

    const updateMatch = () => {
        matches.value = mediaQuery.matches;
    }

    const getMediaQuery = (query: ScreenSize | MediaQuery): string => {
        if (typeof query === 'string') {
            if (!screens[query]) {
                throw new Error(`Screen size ${query} is not defined in screens.`);
            }
            return `(min-width: ${screens[query]})`;
        } else if (typeof query === 'object' && query.type && query.size) {
            const size = screens[query.size];
            if (!size) {
                throw new Error(`Screen size ${query.size} is not defined in screens.`);
            }
            if (query.type === 'up') {
                return `(min-width: ${size})`;
            } else if (query.type === 'down') {
                return `(max-width: ${size})`;
            } else {
                throw new Error(`Query type ${query.type} is not supported.`);
            }
        } else {
            throw new Error('Invalid query format.');
        }
    }

    onMounted(() => {
        mediaQuery.addEventListener('change', updateMatch);
    });

    onBeforeMount(() => {
        mediaQuery = window.matchMedia(getMediaQuery(query));
        updateMatch();
    })

    onUnmounted(() => {
        mediaQuery.removeEventListener('change', updateMatch);
    });

    return matches;
}

export default useMediaQuery;

Explanation

  1. Initialization and Lifecycle Hooks:

    • onBeforeMount: This hook is used to set the initial value of matches correctly before the component mounts, preventing incorrect initial values that trigger unwanted animations.
    • onMounted and onUnmounted: These hooks add and remove event listeners for media query changes.
  2. Media Query Handling:

    • getMediaQuery: This function generates the appropriate media query string based on the input.
    • updateMatch: Updates the matches ref with the current state of the media query.
  3. Error Handling:

    • The composable throws errors if the provided query format is invalid or if the screen size is not defined in the screens object.

Integration with Tailwind CSS

To seamlessly integrate this composable with Tailwind CSS, you can define your breakpoints in Tailwind's configuration. Here's an example configuration:

module.exports = {
    theme: {
        screens: {
            tablet: '640px',
            laptop: '1024px',
            desktop: '1280px'
        },
        // ...other configurations
    },
    // ...other configurations
}

Combining Tailwind CSS with the Composable

By aligning the breakpoints in your composable with those in Tailwind CSS, you ensure consistency across your application's responsive design. This composable can now be used to conditionally render components based on the current screen size, matching Tailwind's utility classes.

Usage Example

Here’s an example of how to use the useMediaQuery composable in a Vue component:


<template>
  <div v-if="isDesktop">
    <p>This content is only visible on desktop screens.</p>
  </div>
</template>

<script setup>
  import {useMediaQuery} from './composables/useMediaQuery';

  const isDesktop = useMediaQuery({type: 'up', size: 'desktop'});
</script>

Creating a reusable media query composable in Vue not only simplifies responsive design but also ensures consistency when integrated with Tailwind CSS. This composable efficiently handles media queries, providing immediate and accurate results to prevent unwanted re-renders and animations.

By following this guide, you can enhance your Vue applications' responsiveness, creating a seamless and adaptive user experience.


profile

Felix Astner

As a software developer, I bring a specialized focus in web technologies, enriched by my interests in C++, AI, and computer theory. If you're in need of any freelance services, expert training, or insightful consulting, I encourage you to connect with me.

HomePrivacyImpressum