facebook/react · error · Error

Rendered more hooks than during the previous render

Error message

Rendered more hooks than during the previous render

What it means

When a server render re-runs because of render-phase updates, Fizz reuses the hook list built in the previous pass. createHook throws when a re-render must allocate a hook beyond that list — the component called more hooks than the previous render, which is the Rules of Hooks violation 'never call hooks conditionally'.

Source

Thrown at packages/react-server/src/ReactFizzHooks.js:239

        `[${nextDeps.join(', ')}]`,
        `[${prevDeps.join(', ')}]`,
      );
    }
  }
  // $FlowFixMe[incompatible-use] found when upgrading Flow
  for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    // $FlowFixMe[incompatible-use] found when upgrading Flow
    if (is(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }
  return true;
}

function createHook(): Hook {
  if (numberOfReRenders > 0) {
    throw new Error('Rendered more hooks than during the previous render');
  }
  return {
    memoizedState: null,
    queue: null,
    next: null,
  };
}

function createWorkInProgressHook(): Hook {
  if (workInProgressHook === null) {
    // This is the first hook in the list
    if (firstWorkInProgressHook === null) {
      isReRender = false;
      firstWorkInProgressHook = workInProgressHook = createHook();
    } else {
      // There's already a work-in-progress. Reuse it.
      isReRender = true;
      workInProgressHook = firstWorkInProgressHook;

View on GitHub (pinned to eafeac097b)

Solutions

  1. Move every hook above conditionals and early returns so the same hooks run in the same order every render
  2. Replace conditional hooks with conditional use of results: call the hook unconditionally, then branch on its value
  3. Enable eslint-plugin-react-hooks (rules-of-hooks) so this class of bug fails statically

Example fix

// before
function Detail({item}) {
  if (!item) return null;
  const [tab, setTab] = useState(0); // hook after early return
  return <div>{item[tab]}</div>;
}

// after
function Detail({item}) {
  const [tab, setTab] = useState(0); // always called
  if (!item) return null;
  return <div>{item[tab]}</div>;
}
Defensive patterns

Strategy: validation

Validate before calling

// .eslintrc: catch conditional hooks statically before any SSR run
// {
//   "extends": ["plugin:react-hooks/recommended"]
// }
// Rules of Hooks violations (conditional hooks, early returns above hooks) fail lint instead of SSR.

Prevention

When it happens

Trigger: Conditional hooks in a server-rendered component: if (cond) useState(...), a hook placed after an early return, or hooks inside loops with varying iteration counts. The violation surfaces on the re-render pass triggered by a render-phase update (a setter called during render).

Common situations: Client components with conditional hooks being SSR'd; refactors that introduce an early return above existing hooks; derived-state code whose setState during render triggers the second pass that exposes the order drift.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/9a00a12efb5134ca. Report an issue: GitHub.