Rendered more hooks than during the previous render

A component called a different number of hooks than on its last render, usually because a hook sits after an early return or inside an if.

Browser console
Rendered more hooks than during the previous render.

Also appears as

  • Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
  • React has detected a change in the order of Hooks called by Profile. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://react.dev/link/rules-of-hooks
  • Minified React error #310; visit https://react.dev/errors/310 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

Prompt to fix it

My React app crashes with "Rendered more hooks than during the previous render." Find the component where a hook runs only some of the time: after an early return, inside an if, &&, ternary, or loop, or inside a component that is called as a plain function. Move every hook to the top of the component above the first return, and move the condition inside the hook, for example inside the effect or as a query’s enabled option. Don’t disable the rules-of-hooks lint rule.

Paste into Layerwise or any AI coding tool.

What it means

React doesn’t know your hooks by name. It remembers them by position: the first hook in a component is slot one, the next is slot two, and so on. That only works if the component calls the same hooks in the same order on every render.

The usual cause is an early return above a hook, such as if (!user) return <Spinner />; followed by useEffect(...). The first render returns early and calls fewer hooks. Once the data arrives, the component reaches the extra hook, and React finds a slot it has never seen.

The fix is always the same: put every hook above the first return, outside any condition or loop. Move the condition inside the hook instead, for example inside the effect or into a query’s enabled option.

Common causes

  • An early return for loading, an error, or a missing user sits above a useState, useEffect, or custom hook.
  • A hook is called inside an if, after &&, or in one branch of a ternary, so it runs only some of the time.
  • A hook is called inside a loop or .map() whose length changes between renders.
  • A component that uses hooks is called like a function, as in {open && Panel()}, so its hooks become part of the parent’s list only some of the time.

How to fix it

  1. Open the component in the error and find its first return and every condition that comes before a hook.
  2. Move all hooks to the top of the component, above any early return.
  3. Move conditions inside the hooks: check them inside useEffect, or pass an option like enabled: Boolean(userId) to a data-fetching hook.
  4. Render components as elements, <Panel />, so each one keeps its own hooks.
  5. Turn on the react-hooks/rules-of-hooks ESLint rule so the editor flags this while you edit.

Example fix

import { useEffect } from 'react';

type User = { name: string };

export function Profile({ user }: { user?: User }) {
  // Every hook runs on every render, before any early return.
  useEffect(() => {
    if (user) document.title = user.name;
  }, [user]);

  if (!user) return <p>Loading...</p>;
  return <h1>{user.name}</h1>;
}
All errors
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.

Invalid hook call

A hook such as useState ran somewhere React can’t track it, or the app loaded two copies of React that don’t know about each other.

Too many re-renders. React limits the number of renders to prevent an infinite loop.

Too many re-renders

A component changes its own state while it renders, so React renders it again and again until it stops the loop.

Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: - A server/client branch `if (typeof window !== 'undefined')`. - Variable input such as `Date.now()` or `Math.random()` which changes each time it's called. - Date formatting in a user's locale which doesn't match the server. - External changing data without sending a snapshot of it along with the HTML. - Invalid HTML tag nesting. It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. https://react.dev/link/hydration-mismatch

Hydration failed

The HTML the server sent didn’t match what React rendered first in the browser, so React threw that part away and rebuilt it on the client.

Each child in a list should have a unique "key" prop. Check the render method of `TodoList`. See https://react.dev/link/warning-keys for more information.

Each child in a list should have a unique "key" prop

A list rendered with map gave its items no stable ID, so React can’t tell which item is which when the list changes.

Build with an agent that checks its work

The Layerwise agent clicks through the live preview and reads the console, so it catches errors before you do.