Material UI useMediaQuery SSR hydration mismatch between server fallback and client viewport
0 reputation · 17 Jul 2021, 09:17 UTC
0 reputation · 17 Jul 2021, 09:17 UTC
Material UI's useMediaQuery hook evaluates CSS media queries in JavaScript, but during server-side rendering it cannot access window.matchMedia and returns the defaultMatches fallback (default false). When the client viewport satisfies the query, the first client render diverges from the server HTML, causing a hydration mismatch in development and a potential layout flash in production.
React 18+ useSyncExternalStore semantics make the server snapshot authoritative for the initial hydration pass, so client-side media state must be reconciled after mount rather than during the first render. The noSsr option suppresses the mismatch by holding the fallback until after mount, but this delays correct responsive behavior and can cause a visible content shift. An unresolved decision is whether to use noSsr, defer media-dependent rendering until after mount, or avoid JavaScript media queries entirely in favor of CSS breakpoints through the sx prop or theme breakpoints.
Because MUI v5, v6, and v7 may differ in default option values and useSyncExternalStore integration, the applicable behavior for a given installation remains version-dependent. Which strategy best balances hydration safety against delayed correct rendering for structural layout decisions, and how should that trade-off be verified for a specific MUI version?
During server-side rendering, useMediaQuery cannot access window.matchMedia and returns the defaultMatches fallback (typically false). When the client viewport satisfies the media query, the first client render diverges from the server HTML, causing hydration mismatch warnings in development and a potential layout flash in production.
The server always returns the fallback value, so if the viewport would match the query on the client, the rendered HTML differs from what React expects after hydration, triggering the mismatch.
To determine the optimal strategy, which MUI version are you using?
Use comments to ask for clarification. Post a solution as an answer.
6,690 reputation · 17 Jul 2021, 21:03 UTC
When useMediaQuery drives structural layout, you're asking React to reconcile two different sources of truth: the server's false fallback and the client's actual viewport state. React 18's useSyncExternalStore makes the server snapshot authoritative during hydration, so any divergence triggers a mismatch.
CSS breakpoints via the sx prop or theme.breakpoints sidestep this entirely. They evaluate in the browser but don't participate in React's hydration reconciliation. The layout shifts only when CSS media queries fire—typically after paint—but that's preferable to a JavaScript-driven hydration error.
Reserve useMediaQuery for non-layout concerns: animation timing, conditional data fetching, or decorative elements that can safely lag behind. For these cases, noSsr: true is acceptable if you're okay with the content shift.
MUI v5, v6, and v7 may differ in defaultMatches defaults and useSyncExternalStore integration. Check your version's documentation and test by comparing server-rendered HTML with client hydration results to confirm your strategy works in practice.