Invalid hook call. Hooks can only be called inside of the body of a function component

Filed under Next.js & React errors

Most likely causeAbout 10 min · 6 steps

Either a hook is being called somewhere hooks are not allowed, or — sneakier — your app has two copies of React installed, and the hook and the renderer are talking to different copies.

Check for hooks in wrong places
Run npm ls react for duplicates
Match react and react-dom versions

Fastest route

Give your AI this prompt

Copy it, replace the bracketed part with your error, and send it.

Preview the prompt
My React app crashes with: "Invalid hook call. Hooks can only be called inside of the body of a function component."

Here is the component that was rendering when it crashed: [paste the component]. Here is what "npm ls react" prints: [paste the output].

Please: 1) check the component for hooks called inside conditions, loops, plain functions, or event handlers, and move any you find to the top level of the component or into a custom hook named use..., 2) read the npm ls output — if it shows more than one copy or version of React, tell me exactly which package is carrying the extra copy and how to make it a peer dependency instead, 3) confirm react and react-dom are set to the exact same version in package.json.

Constraints: tell me which of the three causes you found before changing anything, do not rewrite the component's behavior to work around a duplicate-React problem, and if a clean reinstall is needed, give me the exact commands in order.

Fix it yourself

Follow the repair

  1. Read the whole error — it lists its own three causes. Scroll past the headline and React literally enumerates the suspects: breaking the Rules of Hooks, more than one copy of React, more than one copy of a renderer. Work them in that order instead of randomly rewriting components.
  2. Audit the component that crashed for hooks in illegal spots. Every call to useState, useEffect, or any use... hook must sit at the top level of a function component or a custom hook — never inside an if, a loop, a try block, or a callback. The classic AI-generated version is a conditional hook:
    // Illegal: the hook disappears when the condition is false
    if (user) {
      const [name, setName] = useState(user.name);
    }
    
    // Legal: hook at the top level, condition inside it
    const [name, setName] = useState(user ? user.name : "");
    Also check the call is not inside a plain helper function — if the function uses hooks, its name must start with use and it must be called from a component.
  3. If the code is clean, check for duplicate React. Run this in the project terminal:
    npm ls react
    Healthy output shows one React at one version. If you see two versions, or a react entry nested underneath some other package, you have found it — skip ahead to step 5. Run npm ls react-dom as well and confirm it matches React's version exactly.
  4. Know where duplicates come from. A component library or shared package that lists react as its own dependency instead of a peer dependency installs a private copy. So do monorepo subfolders with their own package.json, and stray manual installs of react inside a sub-package. The app ends up with two Reacts that cannot see each other's hook lists — legal code, instant crash.
  5. Dedupe and align. Fix the source: the library or subfolder should declare React as a peer dependency, not bundle its own. In your app's package.json, set react and react-dom to the exact same version. Then do the clean sweep so only one copy can survive:
    rm -rf node_modules package-lock.json
    npm install
    Re-run npm ls react and confirm a single copy before moving on.
  6. Restart everything, then retest. Stop the dev server fully and restart it — bundlers cache module resolution, and a cached second copy can haunt you after the fix. Hard-refresh the browser too. If the error still appears with exactly one React installed and versions matched, it really is step 2: re-read the crashing component for a hook hiding in a condition or a plain function.
Why this happensOptional explanation

Hooks work because React keeps a private list for the component currently rendering: each useState call takes the next slot on that list. For that list to exist, two things must be true. The hook must run while a function component is rendering, in the same order every time. And the hook and the renderer must be the same copy of React — because the list lives inside one specific copy.

Break the first condition and you get this error honestly: the hook ran inside an if statement, a loop, an ordinary helper function, or a class component, where no list exists. Break the second condition and you get the same error dishonestly: your code is perfectly legal, but a dependency dragged in its own React, the hook registered with copy A, the renderer is running copy B, and from copy B's point of view no component is rendering at all. React cannot tell these apart, so it prints one error for both — which is why perfectly written components crash with a message claiming the hooks are misplaced.

  • Rules of Hooks violation: a hook inside a condition, loop, nested plain function, event handler, or class component.
  • Duplicate React: two copies of React in the installed tree — the cause when the code looks blameless.
  • Mismatched renderer: react and react-dom at different versions, or two renderer copies — same family of problem.
  • Hook in a regular function: a helper named like a normal function calling useState — only components and functions named useSomething may call hooks.

Quick answers

I really am calling a hook in a class component. Now what?

Hooks are designed for function components and custom hooks only, so this one is real: move the state and logic into the component that renders the UI, or convert the class component to a function component. There is no supported bridge that lets a class call hooks directly.

Do I need to delete node_modules and reinstall everything?

Almost never. One tangled dependency can quietly pull in a second React, and npm ls react exposes it in seconds — two version lines or a nested react folder is the smoking gun. Check before you rewrite code; duplicate React is the version of this error people waste evenings not checking.

Why would a library I installed bring its own React?

When an app renders across package boundaries — a component library, a shared package, a monorepo — each side can bundle its own React. Make React a peer dependency of the library and a single real dependency of the app, and in bundlers like webpack or Vite, alias react to one resolved copy.

One fix, every Friday.

One real error, explained in plain English, with the fix — in your inbox every Friday. No spam, no jargon, unsubscribe anytime.