coss.com Svelte

useMediaQuery

Reactive media query helper with Tailwind-like syntax.

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.json

Usage

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:

NameValue
sm640px
md800px
lg1024px
xl1280px
2xl1536px
3xl1600px
4xl2000px

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

PatternExampleMatches
"{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

PropertyTypeDescription
minBreakpoint \| numberMin-width breakpoint name or pixel value
maxBreakpoint \| numberMax-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")
    ≥ 640px false
  • useMediaQuery("md")
    ≥ 800px false
  • useMediaQuery("lg")
    ≥ 1024px false
  • useMediaQuery("xl")
    ≥ 1280px false
  • useMediaQuery("2xl")
    ≥ 1536px false

Max-width (below breakpoint)

  • useMediaQuery("max-sm")
    < 640px false
  • useMediaQuery("max-md")
    < 800px false
  • useMediaQuery("max-lg")
    < 1024px false

Ranges

  • useMediaQuery("sm:max-md")
    640 - 799px false
  • useMediaQuery("md:max-lg")
    800 - 1023px false
  • useMediaQuery("lg:max-xl")
    1024 - 1279px false

Device & preferences

  • useMediaQuery({ pointer: "coarse" })
    touch false
  • useMediaQuery({ pointer: "fine" })
    mouse false
  • useMediaQuery("(prefers-color-scheme: dark)")
    false
  • useMediaQuery("(prefers-reduced-motion: reduce)")
    false

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>