Astro shows “Missing value for client:media directive” when you write client:media without a media query. The directive hydrates a component only when a CSS media query matches, so it needs to know which query. Write it with parentheses, the same as in CSS: client:media="(max-width: 768px)". The full message reads: “Media query not provided for client:media directive.”
Key Takeaways
client:mediaalways takes a CSS media query as its value.- Include the parentheses:
"(max-width: 768px)", not"max-width: 768px".- It controls when JavaScript loads, not whether the HTML shows. Hide the HTML with CSS.
- Typical use: a mobile-only menu drawer or a desktop-only widget.
The fix
---
import MobileMenu from '../components/MobileMenu.tsx';
---
<!-- Wrong -->
<MobileMenu client:media />
<!-- Right -->
<MobileMenu client:media="(max-width: 768px)" />
Any valid media query works:
<DesktopSearch client:media="(min-width: 1024px)" />
<AnimatedHero client:media="(prefers-reduced-motion: no-preference)" />
Hydration vs visibility
client:media only decides when the component’s JavaScript loads and runs. The server-rendered HTML is still in the page on every screen size. If the component should be invisible on desktop, hide it with CSS too:
<div class="mobile-only">
<MobileMenu client:media="(max-width: 768px)" />
</div>
<style>
@media (min-width: 769px) {
.mobile-only { display: none; }
}
</style>
Use the same breakpoint in both places. If the CSS hides at 769px but the directive hydrates up to 900px, tablets download JavaScript for a hidden component.
If the window is resized so the query starts matching, Astro hydrates the component then. It doesn’t un-hydrate when the query stops matching.
Keeping breakpoints in sync
Breakpoints defined in two places drift. Keep the query in one constant:
// src/data/breakpoints.ts
export const mobile = '(max-width: 768px)';
---
import { mobile } from '../data/breakpoints';
---
<MobileMenu client:media={mobile} />
Does it need a framework at all?
Mobile navigation is a common use, and it rarely needs React. A <button> that toggles aria-expanded and a hidden attribute, written in an Astro component <script>, works on every screen size with a few hundred bytes. The Astro components guide has the pattern, and lazy-loading islands compares client:media with client:visible and client:idle. For the CSS side of responsive layouts, see the Astro styling guide. The directives reference has the formal definition.
FAQ
Can I pass a variable to client:media?
Yes. client:media={query} with a string variable works, as shown above.
Does client:media work with Astro components?
No. Client directives only apply to framework components (React, Vue, Svelte, Solid, Preact). Astro components render on the server only.