vue-responsiveness
v0.2.5
Published
A tiny, performant, and intuitive Vue 3 plugin for working with responsive breakpoints and media queries at runtime.
Downloads
2,185
Maintainers
Keywords
Readme
Vue Responsiveness
A tiny, performant, and intuitive Vue 3 plugin for working with responsive breakpoints and media queries at runtime.
Use it when you want Vue components to react to breakpoints, orientation, hover support, color scheme, reduced motion, and contrast preferences without manually wiring window.matchMedia.
Features
- Vue-friendly API: use
$matchesin templates oruseMatches()in<script setup>. - Breakpoint presets included: start with Bootstrap 5 by default, or switch to Tailwind CSS, Vuetify, Bulma, and more.
- Custom breakpoints: define your own app-specific intervals with a plain object.
- Media query support: react to viewport width,
orientation,hover,prefers-color-scheme,prefers-contrast, andprefers-reduced-motion. - SSR safe: it does not touch
windowduring server rendering. - Zero runtime dependencies: lightweight and built on the browser's native
window.matchMedia. - Automatic cleanup: listeners are attached to
MediaQueryListobjects and cleaned up with the Vue app.
Installation
Use the package manager your project already uses.
| Package manager | Command |
|-----------------|---------|
| pnpm | pnpm add vue-responsiveness |
| yarn | yarn add vue-responsiveness |
| npm | npm install vue-responsiveness |
Quick start
Demo
1. Register the plugin
In main.ts:
import { createApp } from 'vue'
import App from './App.vue'
import { VueResponsiveness } from 'vue-responsiveness'
createApp(App)
.use(VueResponsiveness)
.mount('#app')That is enough to use the default Bootstrap 5 breakpoints. For other frameworks (Tailwind CSS, Vuetify, etc.), or your own intervals, see Breakpoint management.
2. Choose how to read responsive state
You can use either option below, or mix them in the same app. Use $matches for simple template conditions and useMatches() when you need responsive state in <script setup>.
Option A: use $matches in templates
Every component can read $matches in its template, without importing.
<template>
<div>
<p>Current breakpoint: {{ $matches.current || 'unknown' }}</p>
<p v-if="$matches.md.min">
This is visible on medium screens and larger.
</p>
<p v-if="$matches.sm.max">
This is visible on small screens and smaller.
</p>
<p v-if="$matches.prefers.colorScheme === 'dark'">
Dark mode is preferred.
</p>
</div>
</template>Option B: use useMatches() in script setup
Use the composable when you need responsive state in computed values, watchers, or methods.
<script setup lang="ts">
import { computed } from 'vue'
import { useMatches } from 'vue-responsiveness'
const matches = useMatches()
const showSidebar = computed(() => matches.md.min)
</script>
<template>
<main>
<aside v-if="showSidebar">
Sidebar navigation
</aside>
<nav v-else>
Mobile navigation
</nav>
</main>
</template>You can also use helper methods:
const showDesktopNav = computed(() => matches.isMin('lg'))
const showMobileNav = computed(() => matches.isMax('sm'))
const isTabletLayout = computed(() => matches.isOnly('md'))Breakpoint management
Use a preset
The plugin uses Presets.Bootstrap_5 by default. To use a different preset, pass it as the second argument to app.use().
import { createApp } from 'vue'
import App from './App.vue'
import { VueResponsiveness, Presets } from 'vue-responsiveness'
createApp(App)
.use(VueResponsiveness, Presets.Tailwind_CSS)
.mount('#app')Available presets:
Ant_Design, Bootstrap_3, Bootstrap_4, Bootstrap_5, Bulma, Chakra, Foundation, Ionic, Master_CSS, Material_Design, Materialize, Material_UI, Quasar, Semantic_UI, Skeleton, Tailwind_CSS, Vuetify, Windi_CSS
Presets.Bootstrap_5 = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1400,
}Vue Responsiveness is designed to have one app-wide breakpoint configuration. Register the plugin once, close to createApp(), so all components read from the same $matches object.
Define custom breakpoints
Pass an object whose keys are your breakpoint names and whose values are the minimum width in pixels. The plugin calculates each breakpoint's maximum width from the next breakpoint.
app.use(VueResponsiveness, {
small: 0,
medium: 777,
large: 1234
})Then use those names in templates:
<!-- @media (min-width: 777px) and (max-width: 1233.9px) -->
<template v-if="$matches.medium.only">
<TwoColumnLayout />
</template>API Reference
$matches and useMatches() expose the same reactive object. Use $matches in templates and useMatches() in JavaScript or TypeScript.
Breakpoint status properties
Each breakpoint key, such as sm, md, lg, or one of your custom names, contains three booleans.
| Name | Type | Description | Example Access |
|-----------------------|-----------|-------------|----------------|
| current | string | The breakpoint interval that currently matches. | $matches.current |
| [breakpointKey].min | boolean | true at this breakpoint width and above. | $matches.md.min |
| [breakpointKey].max | boolean | true up to and including this breakpoint range. | $matches.lg.max |
| [breakpointKey].only| boolean | true only inside this breakpoint range. | $matches.sm.only |
Breakpoint helper methods
These are handy in computed values and methods.
| Name | Type | Description | Example usage |
|---------------------|------------------------------|-------------|---------------------------------------|
| isMin(breakpoint) | (breakpointKey) => boolean | Matches at this breakpoint and above. | matches.isMin('md') |
| isMax(breakpoint) | (breakpointKey) => boolean | Matches inside this breakpoint range and below. | matches.isMax('sm') |
| isOnly(breakpoint)| (breakpointKey) => boolean | Matches only inside this breakpoint range. | matches.isOnly('lg') |
Additional media query properties
These values mirror common CSS media queries, but are available directly in Vue.
| Name | Type | Description | Media query equivalent |
|-------------------------|-----------------------------------|-------------|------------------------|
| orientation | 'portrait' \| 'landscape' | The current device orientation | @media (orientation: *) |
| hover | 'none' \| 'hover' | The hover capability of the device | @media (hover: *) |
| prefers.reducedMotion | 'no-preference' \| 'reduce' | User preference for reduced motion | @media (prefers-reduced-motion: *) |
| prefers.colorScheme | 'light' \| 'dark' | User preference for color scheme | @media (prefers-color-scheme: *) |
| prefers.contrast | 'no-preference' \| 'more' \| 'less' \| 'custom' | User preference for contrast | @media (prefers-contrast: *) |
Testing
When testing a component that uses $matches, install the plugin through Vue Test Utils global.plugins.
import { shallowMount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'
import { VueResponsiveness } from 'vue-responsiveness'
describe('<MyComponent />', () => {
it('should render', () => {
const wrapper = shallowMount(MyComponent, {
global: {
plugins: [VueResponsiveness]
}
})
expect(wrapper.exists()).toBe(true)
})
})How it works
- Vue Responsiveness uses
window.matchMedia(queryString), the same browser API behind CSS media queries. - It listens to
changeevents onMediaQueryListobjects, so updates happen only when a query starts or stops matching. - It avoids global
resizelisteners and keeps one responsive state object per Vue app. - It is SSR safe because browser-only APIs are read after the plugin is installed in the client.
Questions and issues
Found something confusing, broken, or missing? Open an issue.
