react use() hook infinite loop: fix new promise every render
Your Suspense fallback spins forever and DevTools shows no error — the classic sign of a React use() hook infinite loop from a new promise on every render. Here's the fix.
Your app renders, the Suspense fallback appears, and then... nothing. The spinner spins forever. No console error. No red overlay. React DevTools shows your component re-rendering again and again, and the Network tab fills with identical requests. If you passed a fresh fetch() call into React 19's use() hook, you've built an infinite re-suspension loop — and it's invisible to every error tool you own. What the error looks like: the react use() hook infinite loop (new promise every render) There is no error message for this bug, which is exactly why it's so maddening. What you see: A <Suspense> fallback that never clears. No console errors, no stack trace, no crash — just a spinner. The Network tab shows the same request firing over and over, dozens of times. React DevTools shows the component's render count climbing while nothing on screen changes. The code that causes it usually looks innocent: // ❌ BROKEN: new promise created on every render function UserProfile({ userId }: { userId: string }) { const user = use(fetch(`/api/users/${userId}`).then((r) => r.json())); return <h1>{user.name}</h1>; } Every render calls fetch() again, producing a brand-new promise. use() sees a pending promise, suspends, React waits for it to resolve, re-renders — and the render creates yet another promise. The loop has no exit. Why it happens use() is stateless: unlike useState , it doesn't keep a fixed call order, which is why React 19 lets you call it inside conditionals. But that flexibility comes with a contract — the promise you pass must have a stable identity across renders. React attaches the promise's pending/resolved/rejected state to the specific promise instance. When render #1 creates Promise A and render #2 creates Promise B, React has no…