This error occurs when React detects that your component calls a different number of hooks between renders, violating the Rules of Hooks. Most commonly caused by conditional hook calls or early return statements before all hooks have been called.
The "Rendered fewer hooks than expected" error is React's way of telling you that the order and number of hooks being called has changed between renders. React hooks rely on a consistent call order to maintain component state correctly across renders. React internally uses the order of hook calls to associate each hook with its corresponding state. When you call hooks conditionally or after an early return statement, React cannot match up the hooks with their previous state, causing this error. This error is part of React's enforcement of the Rules of Hooks, which require that hooks be called in the exact same order on every render. When fewer hooks are called than expected, it indicates a violation of these fundamental rules.
Ensure all hooks are called before any conditional logic or return statements. React requires hooks to be called in the same order every render.
❌ Incorrect - hooks after early return:
function MyComponent({ user }) {
// Early return happens first
if (!user) {
return <div>Loading...</div>;
}
// Hook is called conditionally - this will cause the error
const [count, setCount] = useState(0);
return <div>{count}</div>;
}✅ Correct - hooks at the top:
function MyComponent({ user }) {
// All hooks called first, unconditionally
const [count, setCount] = useState(0);
// Early return comes after hooks
if (!user) {
return <div>Loading...</div>;
}
return <div>{count}</div>;
}Never wrap hooks in conditional statements. If you need conditional behavior, put the condition inside the hook's logic instead.
❌ Incorrect - conditional hook:
function MyComponent({ isEnabled }) {
const [data, setData] = useState(null);
// Conditional hook call - violates Rules of Hooks
if (isEnabled) {
useEffect(() => {
fetchData().then(setData);
}, []);
}
return <div>{data}</div>;
}✅ Correct - condition inside hook:
function MyComponent({ isEnabled }) {
const [data, setData] = useState(null);
// Hook called unconditionally, logic is conditional
useEffect(() => {
if (!isEnabled) {
return; // Early return inside the effect
}
fetchData().then(setData);
}, [isEnabled]);
return <div>{data}</div>;
}Install and configure the official React hooks ESLint plugin to catch these errors during development.
Install the plugin:
npm install eslint-plugin-react-hooks --save-devAdd to .eslintrc.json:
{
"extends": [
"plugin:react-hooks/recommended"
],
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}The rules-of-hooks rule will highlight violations before runtime, catching issues like conditional hook calls or hooks after early returns.
For components with complex conditional rendering, consider extracting logic into separate components or custom hooks.
Before - problematic conditional logic:
function Dashboard({ user, isAdmin }) {
if (!user) {
return <Login />;
}
// This hook is only called when user exists
const [settings, setSettings] = useState({});
if (!isAdmin) {
return <RegularUserView settings={settings} />;
}
// This hook is only called for admins
const [adminData, setAdminData] = useState(null);
return <AdminView data={adminData} />;
}After - all hooks called consistently:
function Dashboard({ user, isAdmin }) {
// All hooks called every render
const [settings, setSettings] = useState({});
const [adminData, setAdminData] = useState(null);
// Conditional rendering happens after hooks
if (!user) {
return <Login />;
}
if (!isAdmin) {
return <RegularUserView settings={settings} />;
}
return <AdminView data={adminData} settings={settings} />;
}Even better - separate components:
function Dashboard({ user, isAdmin }) {
if (!user) return <Login />;
if (!isAdmin) return <RegularUserDashboard />;
return <AdminDashboard />;
}
function RegularUserDashboard() {
const [settings, setSettings] = useState({});
return <RegularUserView settings={settings} />;
}
function AdminDashboard() {
const [settings, setSettings] = useState({});
const [adminData, setAdminData] = useState(null);
return <AdminView data={adminData} settings={settings} />;
}Understanding React's Hook Internals:
React maintains an internal linked list of hooks for each component instance. During the first render, React creates this list in the order hooks are called. On subsequent renders, React walks through the same list assuming the same order. When the order changes or fewer hooks are called, the list becomes misaligned, causing state corruption.
Custom Hooks and the Rules:
Custom hooks must also follow the Rules of Hooks. They should only be called at the top level of function components or other custom hooks:
// ✅ Correct custom hook
function useUserData(userId) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!userId) return;
setLoading(true);
fetchUser(userId)
.then(setData)
.finally(() => setLoading(false));
}, [userId]);
return { data, loading };
}
// ❌ Incorrect usage - conditional hook call
function MyComponent({ showData, userId }) {
if (showData) {
const { data, loading } = useUserData(userId); // Violates Rules of Hooks
return <div>{data}</div>;
}
return null;
}
// ✅ Correct usage
function MyComponent({ showData, userId }) {
const { data, loading } = useUserData(userId); // Always called
if (!showData) {
return null;
}
return <div>{data}</div>;
}Performance Considerations:
You might be tempted to conditionally call hooks to optimize performance by avoiding unnecessary state. However, React hooks are highly optimized and the overhead of calling them unconditionally is negligible compared to the complexity of conditional logic. Always prioritize correctness over micro-optimizations.
TypeScript and Type Safety:
TypeScript won't catch Rules of Hooks violations at compile time since they're runtime constraints. Always rely on the ESLint plugin for static analysis of hook usage patterns.
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
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React