This ESLint error occurs when React Hooks (like useState, useEffect) are called outside function components or custom hooks, or not at the top level. React requires hooks to follow strict rules about where and how they can be called to maintain state consistency.
This error is triggered by the eslint-plugin-react-hooks rules-of-hooks rule when you violate the Rules of Hooks. React Hooks must be called in specific locations to work correctly. React Hooks rely on a consistent call order to maintain state between renders. When you call hooks in conditional statements, loops, nested functions, or outside of React components, React cannot guarantee this consistency, leading to bugs and unpredictable behavior. The Rules of Hooks are enforced by ESLint to catch these issues during development before they cause runtime errors. This rule helps ensure your components follow React's internal assumptions about how hooks work.
Ensure all hooks are called at the top level of your component, before any early returns or conditional logic.
❌ Incorrect - Hook inside condition:
function MyComponent({ isLoggedIn }) {
if (isLoggedIn) {
const [user, setUser] = useState(null); // ❌ Hook in conditional
}
return <div>Content</div>;
}✅ Correct - Hook at top level:
function MyComponent({ isLoggedIn }) {
const [user, setUser] = useState(null); // ✅ Hook at top level
if (!isLoggedIn) {
return <div>Please log in</div>;
}
return <div>Welcome {user?.name}</div>;
}Don't call hooks inside event handlers, timers, or other callback functions. Instead, call them at the component's top level and use the values in callbacks.
❌ Incorrect - Hook in event handler:
function MyComponent() {
const handleClick = () => {
const [count, setCount] = useState(0); // ❌ Hook in callback
setCount(count + 1);
};
return <button onClick={handleClick}>Click</button>;
}✅ Correct - Hook at top level:
function MyComponent() {
const [count, setCount] = useState(0); // ✅ Hook at top level
const handleClick = () => {
setCount(count + 1); // ✅ Use state setter in callback
};
return <button onClick={handleClick}>Count: {count}</button>;
}If you have a regular function that needs to use hooks, convert it to a custom hook by prefixing the name with "use" and ensuring it's called from a component.
❌ Incorrect - Regular function with hooks:
function fetchUserData(userId) {
const [data, setData] = useState(null); // ❌ Hook in regular function
useEffect(() => {
// fetch logic
}, [userId]);
return data;
}
function MyComponent({ userId }) {
const userData = fetchUserData(userId); // ❌ Calling regular function
return <div>{userData}</div>;
}✅ Correct - Custom hook:
function useFetchUserData(userId) { // ✅ "use" prefix makes it a hook
const [data, setData] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(setData);
}, [userId]);
return data;
}
function MyComponent({ userId }) {
const userData = useFetchUserData(userId); // ✅ Calling custom hook
return <div>{userData}</div>;
}Never call hooks inside loops. If you need multiple state values, call useState multiple times or use a single state object/array.
❌ Incorrect - Hooks in loop:
function MyComponent({ items }) {
const states = [];
items.forEach(item => {
states.push(useState(item)); // ❌ Hook in loop
});
return <div>...</div>;
}✅ Correct - Single state for array:
function MyComponent({ items }) {
const [itemStates, setItemStates] = useState(items); // ✅ Single hook
return (
<div>
{itemStates.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}React treats functions starting with uppercase letters as components. If your function uses hooks, it must start with a capital letter or "use".
❌ Incorrect - lowercase component with hooks:
function myComponent() { // ❌ lowercase name
const [value, setValue] = useState(''); // Hook in non-component function
return <input value={value} onChange={e => setValue(e.target.value)} />;
}✅ Correct - Capitalized component:
function MyComponent() { // ✅ Capitalized name
const [value, setValue] = useState('');
return <input value={value} onChange={e => setValue(e.target.value)} />;
}Ensure you have the React Hooks ESLint plugin installed to catch these issues during development.
Install the plugin:
npm install eslint-plugin-react-hooks --save-devConfigure in .eslintrc.json:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}This plugin is included by default in Create React App and many React frameworks.
Why the Rules of Hooks exist:
React Hooks rely on a stable call order across renders. React internally maintains a list of hooks for each component instance, and uses the order of hook calls to associate state with the correct useState or useEffect call. If hooks are called conditionally or in loops, this order can change between renders, causing React to lose track of which state belongs to which hook.
Custom hooks naming:
Custom hooks must start with "use" (e.g., useFetchData, useAuth) for two reasons: (1) it signals to ESLint that the function can call other hooks, and (2) it communicates to other developers that the function follows the Rules of Hooks and should only be called from components or other custom hooks.
Class components and hooks:
Hooks are fundamentally incompatible with class components because they rely on functional component rendering behavior. If you need to use hooks in a class component, you must either refactor the class to a function component or extract the hook logic into a custom hook and pass the values as props.
ESLint limitations:
The rules-of-hooks ESLint rule uses static analysis and can sometimes produce false positives. For example, if you dynamically generate component names or use non-standard patterns, ESLint might incorrectly flag valid code. In rare cases, you can disable the rule for specific lines, but ensure your code actually follows the Rules of Hooks first.
Multiple React instances:
If you have multiple copies of React in your node_modules (often due to npm link or conflicting dependencies), hooks may fail at runtime even if ESLint doesn't complain. Use npm ls react or yarn why react to check for duplicate React installations.
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency