DEV Community

Nadeem Ur-Rehman
Nadeem Ur-Rehman

Posted on

Your Server Rendered Tuesday. Your Client Rendered Wednesday.

Your server rendered Tuesday. Your client rendered Wednesday. The user saw the page flicker like a haunted television, and somewhere a PM wrote "the page blinks on load" in a bug ticket. That ticket is the interview question. It just showed up in production first.

A hydration mismatch is simpler than people make it sound. Server-side rendering produces HTML. The client re-renders the same components and compares. If the two outputs disagree, even by one character, React panics and throws the whole server HTML away and re-renders everything client-side. You lose the entire performance win you added SSR for. The user gets a flash, a layout jump, and your careful performance budget goes up in smoke.

In the interview, "what causes a hydration mismatch" is not a trivia question. It is a filter for "has this person ever shipped SSR to real users." The causes are always the same three. Random values: Math.random(), Date.now(), or a UUID generated during render, so the server snapshot and the client snapshot can never agree. Dates: new Date().toLocaleString() renders in the server's timezone, then re-renders in the browser's timezone, and the two strings disagree by hours. And browser-only APIs: touching window, localStorage, or matchMedia during render throws or returns something different on the server. Every single one of these is the same bug wearing a different hat. Your render function is not deterministic.

Here is the non-obvious part, and it is the thing that actually scores the interview points. A hydration mismatch is not a framework quirk. It is the framework telling you, bluntly, that your render output depends on something outside the component's inputs. It is a purity check. People treat it like a config problem and slap suppressHydrationWarning on the element. That prop is a confession, not a fix. It silences the warning while the underlying mismatch still costs you the full client re-render. The right mental model: if a component can disagree with itself across two renders, it will eventually disagree with your users.

The practical fix is a four-line rule you can apply to any codebase tonight.

// Hydration survival rule: the render must be a pure function of props.
// 1. No Math.random(), Date.now(), or UUIDs during render. Move them to useEffect, or seed them from props.
// 2. No browser-only APIs (window, localStorage, matchMedia) during render. Guard behind `typeof window !== "undefined"` or render after mount.
// 3. No locale or timezone-dependent formatting during render. Format in a client-only component, or pass a fixed timezone from the server.
// 4. No conditional rendering that differs between server and client (extension-dependent markup, client-side A/B flags). Resolve it before first render or gate it behind a mounted state.
Enter fullscreen mode Exit fullscreen mode

And for the bonus point that separates seniors from staff: React 18's selective hydration means you do not have to fix the whole page at once. Wrap the unstable part in a Suspense boundary and the framework hydrates around it. Fail small, not everywhere.

I broke this down in 40 seconds here: https://www.youtube.com/shorts/3MmFVFFEJxw

It is question 12 of 60 in the Frontend System Design Interview Playbook. The full playbook with all 60 questions is here: https://bittalk.gumroad.com/l/frontend-interview-playbook

Top comments (0)