Maximum update depth exceeded means React is being driven through a post-render feedback loop. An effect, layout effect, or callback writes state, the commit happens again, and the same trigger comes back with a fresh render snapshot.
This is different from a setter call during render. The loop happens after the DOM update, usually because the dependency list keeps changing identity or because the effect writes state that it also depends on.
The clean fix is usually to remove mirrored state, stabilise the dependency value, or only write when the next value is actually different from the current one.
Maximum update depth exceeded means React detected an update loop. A state update triggers render, render or effect triggers another state update, and the cycle repeats until React stops it.
The page should always teach dependency identity. Functions, arrays, and objects created during render are new references on every render, so effects that depend on them can run repeatedly even when their visible contents look unchanged.
React finishes rendering, commits the DOM, and then runs effects. If one of those effects writes state, React schedules another render. If the effect depends on a value that keeps changing identity, the same effect runs again and writes state again. The cycle keeps repeating until React aborts it.
The defining behavior is feedback, not speed. The component is not merely rendering often; it is feeding its own output back into another render.
A common trigger is an effect that fetches or derives data, then writes that result into state while depending on an object, array, or callback that is recreated on every render. Another version is a child effect that writes back into parent state, which recreates the child props and restarts the same path.
A more subtle version appears when a component keeps a second copy of a value that already exists in props. The effect mirrors props into state, the render sees a new state object, and the cycle continues.
Use primitives in dependency arrays whenever the effect only needs a small scalar value such as an id or status. If a structured value is necessary, memoize it so React sees the same reference until one of the real inputs changes.
When the update depends on the previous value, use the functional updater form. When the value can be calculated directly from props or other state, delete the mirrored copy and compute it inline instead.
function QueueBanner({ ticket }) {\n const [label, setLabel] = useState("");\n const options = { ticketId: ticket.id };\n\n // Wrong options is recreated every render\n useEffect(() => {\n setLabel(ticket.priority + " queue");\n }, [options, label]);\n\n return <p>{label}</p>;\n}\n\nfunction QueueBannerFixed({ ticket }) {\n const [label, setLabel] = useState("");\n const options = useMemo(() => ({ ticketId: ticket.id }), [ticket.id]);\n\n useEffect(() => {\n const nextLabel = ticket.priority + " queue";\n setLabel((current) => (current === nextLabel ? current : nextLabel));\n }, [ticket.priority, options]);\n\n return <p>{label}</p>;\n}
On each pass, React creates a fresh render snapshot. After commit, the effect sees that snapshot and decides whether to write state. If the dependency identity keeps changing, the effect has no reason to settle, so React keeps scheduling more renders.
That is why a loop can feel invisible in the code. Each individual line looks fine, but the combination of "new dependency every render" plus "state write in the effect" creates a machine that never stabilises.
Log inside the effect and inside the component body. If the effect fires repeatedly without user action, inspect the dependency list. If the effect only begins after a prop change, check whether that prop is actually a fresh object or callback on every render.
If the state being written is just a transformed copy of something already available, remove the copy. The cleanest fix is often to delete the local state rather than trying to keep it in sync.
The most common cause is a useEffect that updates state and depends on a value that changes because of that update. Another common cause is leaving out the dependency array, causing the effect to run after every render.
The dependency array should describe the values used by the effect. If adding a dependency creates a loop, the problem may be unstable references, mirrored state, or logic that should be handled in an event instead of an effect.
Every render creates new object, array, and function literals. If an effect depends on one of those values, React sees it as changed every time. That can rerun the effect and cause another state update.
Move constants outside the component, compute simple values directly, or use useMemo/useCallback for values that genuinely need stable identity. Do not memoize everything; use it when identity is part of the bug or performance need.
function SearchPage() {
const [filters, setFilters] = useState({});
const defaultFilters = { active: true };
useEffect(() => {
setFilters(defaultFilters);
}, [defaultFilters]); // new object every render
return <pre>{JSON.stringify(filters)}</pre>;
}
const defaultFilters = { active: true };
function SearchPage() {
const [filters, setFilters] = useState(defaultFilters);
useEffect(() => {
setFilters(defaultFilters);
}, []);
return <pre>{JSON.stringify(filters)}</pre>;
}
Try this next
0 of 2 completed
React throws this when updates keep triggering more updates after render. The classic version is an effect that sets state, then runs again because that same state or an unstable dependency changed.
"Too many re-renders" usually means the setter ran during render before React could finish the current pass. "Maximum update depth exceeded" usually means React committed a render, then an effect or lifecycle-style update pushed it into another render repeatedly. That difference tells you where to inspect.
React compares dependencies with reference equality. A new object literal such as { page, sort } is a different reference on every render even when the fields are the same. If an effect depends on that object and sets state, each render creates a new dependency, the effect runs again, and the loop continues.
Explore 500+ free tutorials across 20+ languages and frameworks.