implement
Media Queries

Media Queries

A CSS media query as a readable, so a layout can branch on the viewport the way it branches on any other signal.

mediaQuery turns a CSS media query into a Readable<boolean>. It is true while the query matches, and it updates when that changes — so the viewport is just another signal.

import { If, mediaQuery, Span } from "@implementjs/core";

const isMobile = mediaQuery("(max-width: 767px)");

If(isMobile).Then(Span("phone")).Else(Span("desktop"));

Anywhere a readable goes, this goes: If, Switch, a derived, a bound prop.

const dark = mediaQuery("(prefers-color-scheme: dark)");
const reduced = mediaQuery("(prefers-reduced-motion: reduce)");
const wide = mediaQuery("(min-width: 1280px)");

Div({ class: derived([wide], (wide) => (wide ? "grid-cols-3" : "grid-cols-1")) });

It listens only while listened to

The matchMedia listener is attached when something subscribes and removed when the last subscriber leaves — the same lifetime rule derived follows. So one declared at module scope and imported in two places costs one listener, and a tree that unmounts leaves none behind.

// module scope is fine: nothing is attached until something reads it reactively
export const isMobile = mediaQuery("(max-width: 767px)");

Reading it with get() outside a subscription asks matchMedia directly and attaches nothing.

The server, and hydration

There is no viewport on the server, so mediaQuery reports fallback there — false unless you pass one:

const isDesktop = mediaQuery("(min-width: 768px)", { fallback: true });

It keeps reporting the fallback through hydration, which is the part worth knowing. The markup being adopted was rendered against the fallback, and a client render that disagreed would be thrown out and done again — the mismatch warning you would otherwise get for reading matchMedia during a render. The real answer arrives immediately after the pass, as an ordinary update.

That means the fallback is a choice about which layout is worth prerendering, not just a placeholder. Pick the one most readers will see first.

// the desktop tree is the one worth having in the HTML
const isMobile = mediaQuery("(max-width: 767px)");

A browser with no matchMedia at all reports the fallback forever, and never attaches anything.