skip to content

A React component that calls useLayoutEffect produces a warning during server rendering saying the hook does nothing on the server. Why does React warn about this, and what are your options for dealing with it?

level: seniorimportance: should knowfreq 38%

answer

  1. one pass, no DOM, no commit
  2. effects belong to the commit
  3. the promise is about the paint
  4. there is no paint to beat
  5. hydration is when it finally runs

basics

~20 s

Server rendering produces HTML in one pass with no DOM and no commit, so a layout effect cannot run there — the correction it exists to make before the first paint is simply missing. React warns because the server HTML will be visibly wrong until the client hydrates and runs it.

solid answer

~50 s

On the server there is no DOM to mutate and no paint to beat, so React never runs effects of any kind — it renders once to a string or stream and stops. `useLayoutEffect` gets a warning while `useEffect` does not, because a layout effect is by definition the code that must run before the user sees the frame, and on the server that guarantee cannot be honoured: the markup ships uncorrected and the fix only lands after hydration, which reintroduces exactly the flash the hook was chosen to prevent. Your options are to move the work to `useEffect` if it does not need to beat the paint, to render a version of the UI that needs no correction on the first pass and apply the adjustment after mount, or — for a library that must keep the client-side behaviour — to select `useLayoutEffect` on the client and `useEffect` on the server, which silences the warning without changing what the server can do.

go deeper

for a junior

Know the basic fact: no effects run during server rendering, so a layout effect cannot do its job there. Be able to say the first fix you would try is moving the code to useEffect if it does not have to run before the paint.

for a middle

Explain why React warns for one hook and not the other — a layout effect promises to run before the user sees the result, and server HTML reaches the user with no effect having run. Describe the two-pass pattern that renders a neutral version first.

for a senior

Show that you treat the warning as a symptom of a user-visible defect, not lint noise. Weigh demoting the hook, restructuring so the first pass needs no correction, and knowingly silencing it, and say what each choice costs in flash duration and extra renders.

for a principal

Own the policy question: which parts of the app may depend on browser-only corrections at all, given that anything which does cannot be correct in server HTML. Frame it as a rendering-strategy decision with a measurable cost rather than a per-component workaround.

## What server rendering actually does A server render is a single pass. React walks the component tree, calls each function component, and turns the result into HTML — with `renderToString`, or streamed with `renderToPipeableStream`. There is no DOM, no commit phase, no browser, and therefore no paint. Effects are part of the commit, so none of them run: not `useEffect`, not `useLayoutEffect`. Only the render-phase code executes. That is not a limitation React could remove. There is nothing on the server for a layout effect to measure or mutate, and no frame for it to get ahead of. ## Why only one of the two hooks warns If effects never run on the server, why does React single out `useLayoutEffect`? Because of what each hook *promises*. `useEffect` says "run this after the update is visible" — on the server there is no update to be visible, so skipping it is harmless and no warning is issued. `useLayoutEffect` says "run this **before** the user can see the result". A component that uses it is asserting that the DOM as first rendered is wrong and must be corrected before anyone sees it. On the server that assertion cannot be satisfied at all: the HTML the user receives is the uncorrected version. The practical consequence is a real, visible defect. The browser paints the server HTML as soon as it arrives, showing the un-corrected layout. Only when the JavaScript loads and hydration commits does the layout effect finally run and fix things. The user sees the wrong state not for one frame but for however long hydration takes — the exact flash `useLayoutEffect` was chosen to eliminate, made worse. The warning is React telling you that the guarantee you asked for is unavailable on this path. ## The three honest responses **1. It did not need to be a layout effect.** This is the most common case. The work does not actually change what is on screen for the current frame — it starts a timer, records something, syncs with an external system — and someone reached for `useLayoutEffect` because timing felt flaky. Move it to `useEffect` and the warning and the problem both disappear. **2. Do not render the correction-dependent UI on the server.** Restructure so the first pass renders something that is correct without any adjustment, and apply the adjustment after mount. In practice that means the server and the first client render agree on markup, an effect flips a flag, and the corrected version renders on the second pass: ```js const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); // render the plain version until mounted, the adjusted one after ``` This is the honest fix when the adjustment truly depends on the browser, because it accepts that the server cannot know the answer instead of pretending otherwise. Two-pass rendering costs an extra client render and delays the corrected view slightly, so it is worth it only for the part of the tree that genuinely needs it. **3. The isomorphic-layout-effect idiom.** Libraries that must keep pre-paint behaviour on the client, and that render on the server only incidentally, pick the hook by environment: ```js const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; ``` Both hooks have identical signatures, so this is a drop-in swap. Be clear about what it buys: it **silences the warning**. It does not make anything run on the server, and it does not remove the post-hydration correction. It is appropriate when the component's server output is acceptable as-is and the warning is pure noise; it is a cover-up when the server HTML is genuinely wrong, and reaching for it reflexively is the mistake an interviewer is probing for. ## How to talk about it Start from the mechanism — one render pass, no DOM, no commit, so no effects — then explain why the warning targets the pre-paint promise specifically, then give the decision: demote to `useEffect`, restructure so the first pass needs no correction, or knowingly silence it. Saying "just wrap it in a typeof window check" without the reasoning is the weak answer, because it treats a warning about a user-visible defect as a lint annoyance.

  • Does useEffect run during server rendering, and why does it not warn?
    No — no effects run on the server, because effects belong to the commit phase and a server render never commits. It does not warn because `useEffect` only promises to run after the update is visible, and skipping it on the server causes no visible defect. `useLayoutEffect` promises to run before the user sees the result, and that promise is impossible to keep server-side.
  • Does the isomorphic-layout-effect idiom fix the underlying problem or only the warning?
    Only the warning. Choosing `useEffect` on the server changes nothing about what the server can execute — it still runs no effects. The markup still ships uncorrected and the adjustment still lands after hydration. It is legitimate when that server output is acceptable and the warning is noise, and a cover-up when the first paint is genuinely wrong.
  • What does the two-pass approach cost, and when is it not worth it?
    It costs an extra client render and means the corrected view appears slightly after hydration rather than immediately, so the user briefly sees the neutral version. It is not worth it for a large subtree or for something above the fold that would visibly change; there, prefer restructuring so the server can render the correct thing, for example by not depending on browser-only measurements at all.

saying these in an interview costs you the question

  • useEffect runs on the server but useLayoutEffect does not
  • Wrapping it in typeof window makes it run server-side
  • The warning is cosmetic and can always be silenced
  • Server rendering has a commit phase like the client
  • Moving it to useEffect changes when the server renders it

context