A Svelte helper that creates a reactive, SSR-safe media query. It builds on Svelte’s MediaQuery class and preserves the Tailwind-like shorthand from COSS.
Installation
pnpm dlx shadcn-svelte@latest add https://coss-sv.vercel.app/r/use-media-query.jsonUsage
Breakpoint shorthand
Use Tailwind variant syntax to match breakpoints. TypeScript provides full autocomplete. Read the
reactive result through .current.
<script lang="ts">
import { useMediaQuery } from "$lib/hooks/use-media-query.svelte.js";
// Min-width (breakpoint and above) — like md:
const isDesktop = useMediaQuery("md");
// Max-width (below breakpoint) — like max-md:
const isMobile = useMediaQuery("max-md");
// Range (between two breakpoints) — like md:max-lg:
const isTablet = useMediaQuery("md:max-lg");
</script>Object API
Use the object form when you need pointer detection or custom pixel values.
// Touch device detection
const isTouch = useMediaQuery({ pointer: "coarse" });
// Breakpoint + pointer combined
const isMobileTouch = useMediaQuery({ max: "md", pointer: "coarse" });
// Custom pixel values
const isNarrow = useMediaQuery({ max: 600 });Raw media query
Pass any valid CSS media query string as an escape hatch.
const prefersDark = useMediaQuery("(prefers-color-scheme: dark)");
const prefersReducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");Conditional rendering
The primary use case is mounting one component instead of another based on the viewport.
<script lang="ts">
import { useMediaQuery } from "$lib/hooks/use-media-query.svelte.js";
const isDesktop = useMediaQuery("lg");
</script>
{#if isDesktop.current}
<DesktopNav />
{:else}
<MobileNav />
{/if}Breakpoints
The helper includes a static breakpoint map that must match your Tailwind config. Default values:
| Name | Value |
|---|---|
sm | 640px |
md | 800px |
lg | 1024px |
xl | 1280px |
2xl | 1536px |
3xl | 1600px |
4xl | 2000px |
If you override breakpoints in your Tailwind CSS @theme, update BREAKPOINTS in the helper to
match.
API
function useMediaQuery(query: BreakpointQuery | MediaQueryInput | string): MediaQuery;String queries
| Pattern | Example | Matches |
|---|---|---|
"{bp}" | "md" | Viewport ≥ breakpoint |
"max-{bp}" | "max-md" | Viewport < breakpoint |
"{bp}:max-{bp}" | "md:max-lg" | Between two breakpoints |
"(...)" | "(prefers-color-scheme: dark)" | Raw CSS media query |
Object queries
| Property | Type | Description |
|---|---|---|
min | Breakpoint \| number | Min-width breakpoint name or pixel value |
max | Breakpoint \| number | Max-width breakpoint name or pixel value |
pointer | "coarse" \| "fine" | Pointer type (coarse for touch, fine for mouse input) |
Return value
Returns a reactive Svelte MediaQuery. Its .current property is true when the query matches and false otherwise. The server fallback is false.
Examples
Resize the viewport to see values update in real time.
Min-width (breakpoint and above)
useMediaQuery("sm")≥ 640pxuseMediaQuery("md")≥ 800pxuseMediaQuery("lg")≥ 1024pxuseMediaQuery("xl")≥ 1280pxuseMediaQuery("2xl")≥ 1536px
Max-width (below breakpoint)
useMediaQuery("max-sm")< 640pxuseMediaQuery("max-md")< 800pxuseMediaQuery("max-lg")< 1024px
Ranges
useMediaQuery("sm:max-md")640 - 799pxuseMediaQuery("md:max-lg")800 - 1023pxuseMediaQuery("lg:max-xl")1024 - 1279px
Device & preferences
useMediaQuery({ pointer: "coarse" })touchuseMediaQuery({ pointer: "fine" })mouseuseMediaQuery("(prefers-color-scheme: dark)")useMediaQuery("(prefers-reduced-motion: reduce)")
Convenience export
The helper also exports useIsMobile for parity with COSS and shadcn’s use-mobile pattern:
<script lang="ts">
import { useIsMobile } from "$lib/hooks/use-media-query.svelte.js";
const isMobile = useIsMobile(); // equivalent to useMediaQuery("max-md")
</script>