Vue Component Media Queries
Vue Component Media Queries is a lightweight library (less than 1kb gzipped) that provides Vue 2 components for integrating the `window.matchMedia` API directly into your templates and script logic. It is currently at a stable v1.0.0 release. The library differentiates itself by being highly optimized for server-side rendering (SSR), specifically tested with Nuxt.js to prevent hydration errors and support predictive rendering, making it suitable for complex responsive layouts where CSS alone might lead to performance issues or require logic. While there isn't an explicit release cadence, the library has moved from several alpha versions to a stable 1.0.0, suggesting a focus on stability for its current major version. It offers flexibility by allowing media queries to be defined globally via a `MediaQueryProvider` or used for single queries with `MatchMedia`, and can also inject query states into components.
Common errors
-
Error in render: 'mediaQueries' is not defined
cause Attempting to use `MatchMedia` or `this.mediaQueries` injection without a parent `MediaQueryProvider` component in the component tree.fixEnsure `MatchMedia` or any component injecting `mediaQueries` is a descendant of a `MediaQueryProvider` component that has defined the necessary queries. -
Cannot read properties of undefined (reading 'matchMedia')
cause This error typically occurs in a server-side rendering (SSR) environment where `window` is not available, and a `matchMedia` related function is invoked without proper SSR guards or `ssr` prop configuration.fixSet the `ssr` prop on `MediaQueryProvider` or `MatchMedia` to `true` (or a specific boolean value) and ensure `fallback` props are correctly configured to provide a default state during SSR, preventing `window.matchMedia` from being called on the server. -
[Vue warn]: Unknown custom element: <MediaQueryProvider> - did you register the component correctly?
cause The `MediaQueryProvider` or `MatchMedia` component was used in a template but not correctly imported and registered in the Vue component's `components` option.fixImport the component (`import { MediaQueryProvider } from 'vue-component-media-queries'`) and add it to your Vue component's `components` object: `components: { MediaQueryProvider }`.
Warnings
- breaking This library is exclusively designed for Vue 2 applications (peer dependency `^2.6.0`). It is not compatible with Vue 3 due to breaking API changes between Vue major versions.
- gotcha The library integrates `window.matchMedia` and should not be used for simple style changes that can be handled with pure CSS media queries. Using this library when CSS is sufficient can introduce unnecessary JavaScript overhead and complexity.
- gotcha While the library boasts strong SSR support and claims no hydration errors, complex SSR setups or incorrect initial `fallback` prop configurations on `MediaQueryProvider` or `MatchMedia` can still lead to hydration mismatches or unexpected behavior.
- gotcha The documentation explicitly states that the CDN method for including the library is 'not recommended for production usage'. Relying on a global `VueComponentMediaQueries` object can lead to versioning issues, lack of tree-shaking, and slower load times compared to a modern module bundler setup.
Install
-
npm install vue-component-media-queries -
yarn add vue-component-media-queries -
pnpm add vue-component-media-queries
Imports
- MediaQueryProvider
const { MediaQueryProvider } = require('vue-component-media-queries')import { MediaQueryProvider } from 'vue-component-media-queries' - MatchMedia
const { MatchMedia } = require('vue-component-media-queries')import { MatchMedia } from 'vue-component-media-queries' - mediaQueries (injection)
import { mediaQueries } from 'vue-component-media-queries'export default { inject: ['mediaQueries'] }
Quickstart
<template>
<div id="app">
<h1>Responsive Layout Example</h1>
<p>Resize your browser window to see the layout adapt.</p>
<MediaQueryProvider :queries="{
mobile: '(max-width: 680px)',
tablet: '(min-width: 681px) and (max-width: 1024px)',
desktop: '(min-width: 1025px)'
}">
<MatchMedia v-slot="{ mobile, tablet, desktop }">
<div :class="{
'layout-box': true,
'mobile-layout': mobile,
'tablet-layout': tablet,
'desktop-layout': desktop
}">
<h2 v-if="mobile">Mobile Layout</h2>
<h2 v-else-if="tablet">Tablet Layout</h2>
<h2 v-else-if="desktop">Desktop Layout</h2>
<h2 v-else>Default Layout</h2>
<p>This content changes based on media queries.</p>
<p>Current state: {{ mobile ? 'Mobile' : (tablet ? 'Tablet' : (desktop ? 'Desktop' : 'Unknown')) }}</p>
</div>
</MatchMedia>
</MediaQueryProvider>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { MediaQueryProvider, MatchMedia } from 'vue-component-media-queries';
export default Vue.extend({
name: 'App',
components: {
MediaQueryProvider,
MatchMedia,
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.layout-box {
border: 1px solid #42b983;
padding: 20px;
margin: 20px auto;
max-width: 800px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.mobile-layout { background-color: #e0f7fa; color: #00796b; }
.tablet-layout { background-color: #fffde7; color: #fbc02d; }
.desktop-layout { background-color: #e8f5e9; color: #388e3c; }
</style>