Overview
Reactive media query composable with automatic cleanup. Integrates with window.matchMedia for responsive conditionals.
Features
- Window matchMedia integration
- Reactive query string support
- SSR-safe (returns false on server)
- Hydration-aware (defers client updates until hydrated)
- Automatic listener cleanup on scope disposal
- Supports both static and dynamic queries
Function Signature
function useMediaQuery(
query: MaybeRefOrGetter<string>
): MediaQueryContext
Parameters
CSS media query string (can be reactive).
Return Value
matches
Readonly<ShallowRef<boolean>>
Whether the media query currently matches.
The current media query string.
mediaQueryList
Readonly<ShallowRef<MediaQueryList | null>>
The underlying MediaQueryList (null on server).
Stop listening and clean up.
Examples
Static query
import { useMediaQuery } from '@vuetify/v0'
const { matches } = useMediaQuery('(prefers-color-scheme: dark)')
watchEffect(() => {
console.log('Dark mode:', matches.value)
})
Dynamic query
const minWidth = ref(768)
const { matches } = useMediaQuery(() => `(min-width: ${minWidth.value}px)`)
Manual cleanup
const { matches, stop } = useMediaQuery('(hover: hover)')
// Remove listener early
stop()
Responsive layout
const isMobile = useMediaQuery('(max-width: 768px)')
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)')
const isDesktop = useMediaQuery('(min-width: 1025px)')
Portrait orientation
const isPortrait = useMediaQuery('(orientation: portrait)')
Hover capability
const canHover = useMediaQuery('(hover: hover)')
Conditional component rendering
<script setup>
import { useMediaQuery } from '@vuetify/v0'
const isMobile = useMediaQuery('(max-width: 768px)')
</script>
<template>
<MobileNav v-if="isMobile.matches.value" />
<DesktopNav v-else />
</template>
Convenience Functions
usePrefersDark
function usePrefersDark(): MediaQueryContext
Check if the user prefers dark color scheme.
import { usePrefersDark } from '@vuetify/v0'
const { matches: prefersDark } = usePrefersDark()
watch(prefersDark, (dark) => {
document.body.classList.toggle('dark', dark)
})
usePrefersReducedMotion
function usePrefersReducedMotion(): MediaQueryContext
Check if the user prefers reduced motion.
import { usePrefersReducedMotion } from '@vuetify/v0'
const { matches: prefersReducedMotion } = usePrefersReducedMotion()
const transitionDuration = computed(() =>
prefersReducedMotion.value ? 0 : 300
)
usePrefersContrast
function usePrefersContrast(): MediaQueryContext
Check if the user prefers more contrast.
import { usePrefersContrast } from '@vuetify/v0'
const { matches: prefersContrast } = usePrefersContrast()
const contrastClass = computed(() =>
prefersContrast.value ? 'high-contrast' : ''
)
Screen Size
// Mobile
useMediaQuery('(max-width: 768px)')
// Tablet
useMediaQuery('(min-width: 769px) and (max-width: 1024px)')
// Desktop
useMediaQuery('(min-width: 1025px)')
Orientation
// Portrait
useMediaQuery('(orientation: portrait)')
// Landscape
useMediaQuery('(orientation: landscape)')
Display Features
// High DPI screens
useMediaQuery('(min-resolution: 2dppx)')
// Touch capability
useMediaQuery('(hover: none) and (pointer: coarse)')
// Hover capability
useMediaQuery('(hover: hover)')
User Preferences
// Dark mode
useMediaQuery('(prefers-color-scheme: dark)')
// Light mode
useMediaQuery('(prefers-color-scheme: light)')
// Reduced motion
useMediaQuery('(prefers-reduced-motion: reduce)')
// High contrast
useMediaQuery('(prefers-contrast: more)')
SSR Behavior
- During SSR,
matches is always false
mediaQueryList is null on the server
- Updates are deferred until client-side hydration completes
- Query changes during hydration are queued and applied after hydration
Notes
- Automatically removes event listeners on scope disposal
- Reactive query changes are automatically handled
- SSR-safe: returns false during server-side rendering
- Hydration-aware: prevents SSR mismatch by deferring updates