# react-hooks/rules-of-hooks

> Require hooks to be called at the top level of a component or a custom hook

React tells one `useState` from the next by the order you call them in. If a hook runs on one render but not the next, every hook after it gets another hook's state, and React throws "Rendered fewer hooks than expected".

So you must call every hook on every render, in the same order. You can't call one in a condition, a loop, a nested function, or after an early `return`. You also can't call one in a class, an async function, at the top level of a file, or in a function that's neither a component nor a hook. A name counts as a hook if it's `use` or starts with `use` and a capital letter. A component's name starts with a capital letter.

This rule is on by default if your `package.json` depends on `react`.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

On by default as an error. To turn it off:

```toml bunfig.toml
[lint.rules]
"react-hooks/rules-of-hooks" = "off" # or "warn"
```

Every option, set to its default. The ones that are commented out are unset by default.

```toml bunfig.toml
[lint.rules]
"react-hooks/rules-of-hooks" = ["error", {
  # additionalHooks = <string>,
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad */}
```jsx
function Profile({ user }) {
  if (user) {
    const [editing, setEditing] = useState(false);
  }
  return <p>{user?.name}</p>;
}
```

{/* bad */}
```jsx
function Profile({ user }) {
  if (!user) {
    return null;
  }
  const [editing, setEditing] = useState(false);
  return <p onClick={() => setEditing(!editing)}>{user.name}</p>;
}
```

{/* bad */}
```jsx
function List({ items }) {
  return items.map(item => {
    const id = useId();
    return <li key={id}>{item.name}</li>;
  });
}
```

{/* bad */}
```js
function loadTheme() {
  return useContext(ThemeContext);
}
```

Not reported:

{/* good */}
```jsx
function Profile({ user }) {
  const [editing, setEditing] = useState(false);
  if (!user) {
    return null;
  }
  return <p onClick={() => setEditing(!editing)}>{user.name}</p>;
}

function useTheme() {
  return useContext(ThemeContext);
}
```

`use` is the exception. You can call it in a condition or a loop, but not in a `try` block:

{/* good */}
```jsx
function Note({ notePromise }) {
  if (!notePromise) {
    return null;
  }
  const note = use(notePromise);
  return <p>{note.text}</p>;
}
```

A `use` that the file declares itself isn't React's, unless it's imported from `"react"`. Playwright passes a function with that name to every fixture:

{/* good */}
```js
import { test as base } from "@playwright/test";

export const test = base.extend({
  user: async ({ page }, use) => {
    await page.goto("/login");
    await use("alice");
  },
});
```

In a `*.stories.*` file, a function named `render` counts as a component, because Storybook renders it as one:

{/* good {"filename": "Button.stories.jsx"} */}
```jsx Button.stories.jsx
export const Controlled = {
  render: args => {
    const [on, setOn] = useState(false);
    return <Button {...args} active={on} onClick={() => setOn(!on)} />;
  },
};
```

With an ESLint config file both are reported, as `eslint-plugin-react-hooks` does.

You can only call a function made with `useEffectEvent` from an effect or another effect event in the same component:

{/* bad */}
```jsx
function Chat({ room }) {
  const onConnected = useEffectEvent(() => {
    notify(room);
  });
  return <button onClick={onConnected}>Connect</button>;
}
```

Your own effect hooks count as effects if the `additionalEffectHooks` setting is a regular expression that matches their names. It goes under `[lint.settings.react-hooks]` in `bunfig.toml`, or under `settings["react-hooks"]` in an ESLint config.

## Options

| Option | What it does |
| --- | --- |
| `additionalHooks` | Accepted, but this rule doesn't use it. |
