Skip to content

codemonk-digital/vue-responsiveness

Repository files navigation

Vue Responsiveness

What - tiny plugin for working with responsiveness intervals, focused runtime performance and great DX.
Why - I'm obsessed with runtime performance and ease of use: see how it works.

Total Downloads Latest Release License Dependencies unpkg umd min:gzip size Unit tests coverage SSR compatibility status PRs Welcome

Installation

yarn

yarn add vue-responsiveness

npm

npm i vue-responsiveness

Basic demo

Codesandbox

Usage

main.ts

import { VueResponsiveness } from 'vue-responsiveness'

createApp()
   .use(VueResponsiveness)
   .mount('#app')

in any <template />:

<!-- @media (min-width: 576px) -->
<template v-if="$matches.sm.min">
     ...content
</template>

<!-- @media (max-width: 767.9px) -->
<SomeComponent v-if="$matches.sm.max">
  ...content
</SomeComponent>

<!-- @media (min-width: 576px) and (max-width: 767.9px) -->
<div v-if="$matches.sm.only">
  ...content
</div>

Breakpoint presets:

import { VueResponsiveness, Presets } from "vue-responsiveness";

app.use(VueResponsiveness, Presets.Tailwind_CSS)

Note: The default config value is set to Bootstrap 5's responsiveness breakpoints preset.

Preset details:
Presets.Bootstrap_5 = {
  xs: 0,
  sm: 576,
  md: 768,
  lg: 992,
  xl: 1200,
  xxl: 1400,
}

Available presets:

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

Notes:

  • If you maintain a CSS framework (or use one often) and want its preset added, open an issue or a PR.
  • If you spot any inconsistency in the presets (either my typo or some library update), please, let me know, I'll correct it.

Bespoke intervals:

app.use(VueResponsiveness, {
  small: 0,
  medium: 777,
  large: 1234
})
<!-- @media (min-width: 777px) and (max-width: 1233.9px) -->
<template v-if="$matches.medium.only">
  ...content
</template>

Hide components, (while still rendering them) - usage with v-show:

<SomeComponent /> below will be rendered at all times but will only be displayed on md and below:

<!-- rendered at all times (keeps listeners while hidden), but only displayed on 
  @media (max-width: 991.9px) -->
<SomeComponent v-show="$matches.md.max" />

Use in setup() or <script setup>:

import { useMatches } from 'vue-responsiveness'

const matches = useMatches()

const currentInterval = computed(() => matches.interval)
const trueOnSmOnly = computed(() => matches.isOnly('sm'))
const trueOnMdAndAbove = computed(() => matches.isMin('md'))

Testing:

Add plugin to global.plugins when testing components using the plugin's API: Example

import MyComponent from './MyComponent.vue'
import { VueResponsiveness } from 'vue-responsiveness'
describe('<MyComponent />', () => {
  it('should render', () => {
    const wrapper = shallowMount(MyComponent, {
      global: {
        plugins: [VueResponsiveness]
      }
    })
    // test here    
  })
})

How it works:

  • uses the native window.matchMedia(queryString) and only reacts to changes in the query's matches value. It's the same API powering CSS media queries.
  • listeners are placed on the MediaQueryList instances, meaning they are garbage collected as soon as the app is unmounted, without leaving bound events behind on <body> or window object.
  • no global pollution
  • only one instance per app (much lighter than having one instance per component needing it)
  • in terms of memory and/or CPU consumption, using window.matchMadia is a few hundred times lighter than using the "traditional" resize event listener method

Got issues?

Let me know!

Happy coding!
:: }<(((*> ::