media-query
Evaluate a reactive media query without requiring browser globals.
| Package | @vizejs/composable/media-query |
| Own the source | vize lib pull composable:media-query |
| Runtime exports | useMediaQuery, useReducedMotion |
| Gzip budget | 1280 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useMediaQuery, useReducedMotion } from "@vizejs/composable/media-query";
Try it
Adapt recommended guides to available space
Use an actual media-query result to switch a guide list between one and two columns, with a reactive breakpoint selector.
What to observe: Resize the window across 600 pixels: the layout and visible match state change together. At 760 pixels, selecting 800 pixels switches to Compact; selecting 600 pixels restores Roomy. An iframe evaluates its own width.
The query getter follows the writable breakpoint ref. useMediaQuery returns a readonly boolean ref and uses the browser's matchMedia change events. ssrValue: false renders Compact before the browser's first post-render evaluation, so hydration starts from the same markup. Query changes replace the previous subscription, and component disposal removes the final listener. This example changes layout without animation or a simulated host.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { ref, useId } from "vue";
import { useMediaQuery } from "@vizejs/composable/media-query";
const breakpointId = useId();
const breakpoint = ref(600);
const roomy = useMediaQuery(() => `(min-width: ${breakpoint.value}px)`, { ssrValue: false });
</script>
<template>
<div class="composable-example">
<label :for="breakpointId">Roomy layout starts at</label>
<select
:id="breakpointId"
v-model.number="breakpoint"
:aria-describedby="`${breakpointId}-help`"
>
<option :value="600">600 pixels</option>
<option :value="800">800 pixels</option>
<option :value="1000">1000 pixels</option>
</select>
<p :id="`${breakpointId}-help`">
Resize this example's window or change the threshold. In the embedded preview, the query uses
the preview frame's width.
</p>
<output aria-live="polite">Layout: {{ roomy ? "Roomy" : "Compact" }}</output>
<p>Live query: (min-width: {{ breakpoint }}px)</p>
<ul
aria-label="Recommended guides"
:style="{
display: 'grid',
gridTemplateColumns: roomy ? 'repeat(2, minmax(0, 1fr))' : 'minmax(0, 1fr)',
gap: '16px',
padding: '0',
listStyle: 'none',
}"
>
<li>
<h3>Write components</h3>
<p>Start with a template and add reactive behavior.</p>
</li>
<li>
<h3>Check a project</h3>
<p>Use diagnostics to catch mistakes before publishing.</p>
</li>
</ul>
<p>The server renders the compact layout until the browser can evaluate the query.</p>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useMediaQuery |
media | experimental | deterministic-fallback | caller-managed | reactive-scope | web, desktop | window |
— |
useReducedMotion |
media | experimental | deterministic-fallback | caller-managed | reactive-scope | web, desktop | window |
useMediaQuery |
API
useMediaQuery
Evaluate a reactive media query without requiring browser globals. During server rendering (or whenever no capability host resolves) the ref holds the configured server value and no subscription is created. Inside a component the first match is read after mounting, so hydration renders the server value first and never mismatches. The change subscription follows the reactive query and host: each re-evaluation removes the previous listener, and the final listener is removed when the owning reactive scope stops. Call inside an active scope so the subscription is released. A host whose matcher throws propagates the error to the active effect run; the browser default never throws.
function useMediaQuery( query: MaybeRefOrGetter<string>, options: UseMediaQueryOptions = {}, ): Readonly<Ref<boolean>>
useReducedMotion
Return the reactive user motion preference. Shares useMediaQuery semantics: during server rendering the preference is "no-preference" unless ssrValue is true, and the underlying subscription is removed when the owning reactive scope stops.
function useReducedMotion( options: UseMediaQueryOptions = {}, ): ComputedRef<MotionPreference>
Types
MediaQueryHost
Capability required to evaluate media queries.
| Member | Type | Description |
|---|---|---|
matchMedia |
(query: string) => MediaQueryList |
Create an observable result for a media query. |
UseMediaQueryOptions
Options for useMediaQuery.
| Member | Type | Description |
|---|---|---|
ssrValue? |
boolean |
Value exposed when no media-query capability is available. |
host? |
MaybeRefOrGetter<MediaQueryHost | null | undefined> |
Reactive media-query capability for alternate runtimes and tests. |