- Notifications
You must be signed in to change notification settings - Fork1
MatchMedia component library for Vue
License
CyberAP/vue-component-media-queries
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Media Queries component library for Vue.
Key features:
- 🍳Lightweight. Less than 1kb gzippedtotal size.
- 🤗User-friendly. Does not require global installation, can beused on a per-component level.
- 💊Versatile. Works both on a component level (inside
<template>
) or as aninjected property (inside<script>
). - 💡Server Rendered. Zero hydration errors, compatible with Nuxt.js or custom SSR, supportspredictive rendering.
- 🌳Tree-shakeable. Import only the necessary components right where you need them.
- 🧹Clean. Does not pollute your component's context.
Live playground on codesandbox
Define your responsive breakpoints via media queries and place them right in your components:
<!-- App.vue --><template><MediaQueryProvider:queries="{ mobile: '(max-width: 680px)' }"><AppLayout/></MediaQueryProvider></template>
<!-- AppLayout.vue --><template><MatchMediav-slot="{ mobile }"><MobileLayoutv-if="mobile"/><DesktopLayoutv-else/></MatchMedia></template>
There are two ways how you can use Media Queries on Web: CSS and JS runtime.In most cases you'll be fine with just CSS, so before going any further please verify that CSS is insufficient for your case.For example, you don't need this library if you need to toggle element visibility for non-complex elements.You're better off using just CSS for that:
<divclass="show-on-desktop">You're on mobile</div><divclass="show-on-mobile">You're on desktop</div>
@media (min-width:761px) { .show-on-mobile {display: none!important; }}@media (max-width:760px) { .show-on-desktop {display: none!important; }}
But if you encounter a significant performance degradation from rendering everything in a single passor have some logic bound to media queries you might want to usewindow.matchMedia
.This library provideswindow.matchMedia
integration for Vue.
Install library as a dependency:
npm i vue-component-media-queries
Import components via named exports where necessary:
import{MediaQueryProvider,MatchMedia}from'vue-component-media-queries'
If you don't have an option to use NPM you can use a CDN package,it will register a globalVueComponentMediaQueries
variable with appropriate exports.The CDN method is not recommended for production usage. Prefer using NPM method whenever possible.
Import library after Vue:
<scriptsrc="https://unpkg.com/vue@2.6.11/dist/vue.js"></script><scriptsrc="https://unpkg.com/vue-component-media-queries@1.0.0/dist/index.js"></script>
Get components from the global
VueComponentMediaQueries
object:<script>const{ MatchMedia, MediaQueryProvider}=VueComponentMediaQueries;// ...</script>
If you have an app with lots of components that depend on media queries your best option is to useGlobal matching method.
There are two components required for this method:<MediaQueryProvider>
and<MatchMedia>
.
<MediaQueryProvider>
does the actual matching and provides values down the render tree. You should wrap your whole app with this component in yourApp.vue
orLayout.vue
.<MatchMedia>
retrieves these values from the<MediaQueryProvider>
and exposes them to rendering context through scoped slots.You should put this component where you have to actually use these media queries.
Here's a basic setup for this method:
Wrap your app in a
<MediaQueryProvider>
and passqueries
object to it.<template><MediaQueryProvider:queries="$options.queries"><AppLayout/></MediaQueryProvider></template><script>import{MediaQueryProvider}from"vue-component-media-queries";importAppLayoutfrom"./AppLayout.vue";constqueries={mobile:'(max-width: 760px)'};exportdefault{name:'App', queries,components:{ MediaQueryProvider, AppLayout,},}</script>
In any part of your app that's within the
<MediaQueryProvider>
use<MatchMedia>
component to retrieve the results of media queries.<template><MatchMediav-slot="{ mobile }"><MobileLayoutv-if="mobile"/><DesktopLayoutv-else/></MatchMedia></template><script>import{MatchMedia}from"vue-component-media-queries";importMobileLayoutfrom"./MobileLayout.vue";importDesktopLayoutfrom"./DesktopLayout.vue";exportdefault{name:'AppLayout',components:{ MatchMedia, MobileLayout, DesktopLayout,},}</script>
<MatchMedia v-slot="{ mobile }">
in the example above refers to themobile: '(max-width: 760px)'
media query result taken from the<MediaQueryProvider>
.This result is reactive – when you resize the page it will update accordingly.
<MatchMedia>
can be used without<MediaQueryProvider>
.This is convenient if you have just a few components that need media queries.In that case you'll have to pass aquery
directly to<MatchMedia>
and get the result from amatches
slot prop.
<template><MatchMediaquery="(max-width: 760px)"v-slot="{ matches }"><MobileLayoutv-if="matches"/><DesktopLayoutv-else/></MatchMedia></template><script>import{MatchMedia}from"vue-component-media-queries";importMobileLayoutfrom"./MobileLayout.vue";importDesktopLayoutfrom"./DesktopLayout.vue";exportdefault{name:'AppLayout',components:{ MatchMedia, MobileLayout, DesktopLayout,},}</script>
If you have a lot of these components you might prefer usingGlobal matching method instead.
It's possible to have just<MediaQueryProvider>
and no<MatchMedia>
components.This is needed when you want to use your media queries insidecomputed
orwatch
.Provide\Inject pattern can be used to get media queries results.
To get the provided media queries results you'll need to:
Set up
<MediaQueryProvider>
as shown inGlobal matching method.Inject
mediaQueries
in your component:<template><divv-text="title"/></template><script>exportdefault{name:'ResponsiveComponent',inject:['mediaQueries'],computed:{title(){returnthis.mediaQueries.mobile ?`You're on a mobile layout` :`You're on a desktop layout`;}}}</script>
Type:{ [name]: string }
Required:yes
queries
is an object where:
- name is a media query name that would be then used in
<MatchMedia>
scoped slot (<MatchMedia v-slot="{ name }">
) or inmediaQueries
injection (this.mediaQueries.name
). - string value is amedia query.It is handled internally with the
window.matchMedia
API.
constqueries={mobile:'(max-width: 760px)',tablet:'(max-width: 1024px)',desktop:'(min-width: 1024px)',landscape:'(orientation: landscape)'}
queries
are best passed to<MediaQueryProvider>
via the$options
objectbecause$options
contents are static and so should be your media queries object.
<template><MediaQueryProvider:queries="$options.queries"><AppLayout/></MediaQueryProvider></template><script>import{MediaQueryProvider}from'vue-component-media-queries';importAppLayoutfrom'./AppLayout.vue';constqueries={mobile:'(max-width: 760px)',tablet:'(max-width: 1024px)',desktop:'(min-width: 1024px)',landscape:'(orientation: landscape)'};exportdefault{name:'App',components:{ MediaQueryProviderAppLayout,}, queries,// queries can now be used as part of an $options object}</script>
Type:string
orstring[]
A key or a list ofqueries
keys that should returntrue
whenwindow.matchMedia
is unavailable (node.js\nuxt.js for example).
<MediaQueryProvider:queries="{ mobile: '(max-width: 760px)' }"fallback="mobile"><MatchMediav-slot="{ mobile }"> {{ mobile }}<!-- will be true on server, will automatically update on client --></MatchMedia></MediaQueryProvider>
Multiple fallbacks:
<MediaQueryProvider:queries="{ mobile: '(max-width: 760px)', landscape: 'orientation: landscape' }":fallback="['mobile', 'landscape']"></MediaQueryProvider>
Type:boolean
This prop switches between eager and lazy mode for matching to avoid hydration mismatch errors.
false
– Eager mode sets all the queries to match before your components render.It ensures that components using media queries won't have to re-render after a first render due to data mismatch(all media queries returnfalse
before matching, except those listed infallback
prop).true
– Lazy mode allows for components to render with fallback values first (passed tofallback
prop) to avoid hydration errors.
<MediaQueryProvider:queries="{ mobile: '(max-width: 760px)', landscape: 'orientation: landscape' }":fallback="['mobile', 'landscape']"ssr></MediaQueryProvider>
Set this prop totrue
if you have a custom server side rendering.Nuxt.js users will have this set totrue
by default.
Type:string
Default:span
A wrapping tag that is used when a component has more than one child.
<MediaQueryProvider:queries="$options.queries"wrapper-tag="div"><FirstChild/><SecondChild/></MediaQueryProvider>
Type:MediaQueryListEvent
Arguments:
matches
– boolean. Represents media query result.media
— string. Media query.- Rest of
EventTarget
interface.
An even fired when a media queryname
result changes.name
is a key ofqueries
object passed to<MediaQueryProvider>
.
<template><MediaQueryProvider@change:mobile="onMobileChange":queries="$options.queries"><AppLayout/></MediaQueryProvider></template><script>import{MediaQueryProvider}from'vue-component-media-queries';importAppLayoutfrom'./AppLayout.vue';constqueries={mobile:'(max-width: 760px)'};exportdefault{name:'App',components:{ MediaQueryProviderAppLayout,}, queries,methods:{onMobileChange(event){const{ matches, media, ...rest}=event;// some logic},},}</script>
Type:string
, amedia query.
Required: yes, when is not a descendant of<MediaQueryProvider>
.
A media query that needs to be matched in place. Seeusage section for an example.
Type:string
Setsmatches
value totrue
when used outside of browser context (same as for<MediaQueryProvider>
).
<MatchMediaquery="(max-width: 760px)":fallback="isServer"v-slot="{ matches }"> {{ matches }}<!-- will be `true` on server --></MatchMedia>
Type:boolean
Same as for<MediaQueryProvider>
.
Type:string
Default:span
A wrapping tag that is used when a component has more than one child.
<MatchMediav-slot="{ mobile }"><FirstChildv-if="mobile"/><SecondChild/></MatchMedia>
Slot props:{ [name]: boolean }
or{ matches: boolean }
Returns a record of media queries results from the<MediaQueryProvider>
.
<MatchMediav-slot="{ mobile }">{{ mobile }}<!-- a result of `mobile` media query from <MediaQueryProvider> --></MatchMedia>
Or returns amatches
slot prop if you pass aquery
prop.
<MatchMediaquery="(max-width: 760px)"v-slot="{ matches }">{{ matches }}<!-- doesn't need <MediaQueryProvider> --></MatchMedia>
Type:MediaQueryListEvent
Arguments:
matches
– boolean. Represents media query result.media
— string. Media query.- Rest of
EventTarget
interface.
Triggers when a result from passed mediaquery
prop changes.
<MatchMediaquery="(max-width: 760px)"@change="onMedia"/>
Type:{ [name]: boolean }
A record with the results of<MediaQueryProvider>
. SeeProvide\Inject section for an example.
You can use user agent detection (also called browser sniffing) to make a guess which media queries should be matched on server.This is useful when you want to avoidlayout shifts and unnecessary re-renders after a hydration.
To do so, we'll have to parse user agent on server side, set fallback values for<MediaQueryProvider>
and pass them back to the client.
Here's an example of usingua-parser-js
to make a guess which device is sending a request in a default Nuxt.js layout:
<template><MediaQueryProvider:queries="$options.queries":fallback="fallback"><Nuxt/></MediaQueryProvider></template><script>import{MediaQueryProvider}from'vue-component-media-queries';exportdefault{name:'DefaultLayout',queries:{mobile:'(max-width: 760px)'},components:{ MediaQueryProvider},asyncfetch(){if(this.$nuxt.context.req){// ua-parser-js is dynamically imported to avoid including it in the client bundleconst{default:uaparser}=awaitimport('ua-parser-js');const{ device}=uaparser(this.$nuxt.context.req.headers['user-agent']);if(device.type==='mobile'){this.fallback='mobile';}}},data(){return{fallback:null};},}</script>
About
MatchMedia component library for Vue
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.