Skip to main content

useIsHydrated

Detect whether the client has finished hydrating, to safely gate reads of browser/device-only state (window, matchMedia, viewport size) that would otherwise cause an SSR hydration mismatch.

Usage

import { useIsHydrated } from '@multinaire/expo-ui';
import { useWindowDimensions } from 'react-native';

function MyComponent() {
const isHydrated = useIsHydrated();
const { width } = useWindowDimensions();

// Render the same deterministic value the server rendered, then switch to
// the real value right after mount.
const safeWidth = isHydrated ? width : 0;

return <Text>{safeWidth}</Text>;
}

Return Value

TypeDescription
booleanfalse on the server and during the client's hydration render, true before the client's first paint

Why this exists

On web, some hooks (useColorScheme, useWindowDimensions) read real browser/device state synchronously during render. The server can never know that value, so if the client's first render uses the real value while the server rendered a placeholder (or vice versa), React throws a hydration mismatch warning and discards the mismatched markup.

@multinaire/expo-ui uses useIsHydrated internally in ThemeProvider (color scheme) and useResponsiveDesign (viewport dimensions) to keep the client's first render in agreement with the server, then switch over to the real value before the browser paints. It's exported so you can apply the same pattern to your own browser-only reads.

The switch to true happens inside a layout effect, not a passive effect — React resolves it before the hydrated frame is painted, so the placeholder value is never actually visible on screen. This avoids a flash of the placeholder (e.g. a light/dark theme flash on mount) rather than just avoiding the console warning.

Example: Gating a matchMedia read

function usePrefersReducedMotion() {
const isHydrated = useIsHydrated();
const prefersReduced =
typeof window !== 'undefined' &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;

return isHydrated ? prefersReduced : false;
}

Platform Notes

  • On native, there's no SSR step, so this hook resolves to true on virtually the first render — it's a no-op safeguard there.
  • Relevant when using Expo Router's web SSR/SSG output, or any other server-rendered React setup.