React Memory Management
Memory leaks are invisible until they crash production. The app works in development. It dies at scale.
Every subscription, listener, and timer that outlives its component is a leak. React unmount is your last chance to clean up.
Leak Sources
| Source | How It Leaks | Fix |
|---|---|---|
setInterval / setTimeout | Runs after unmount | Clear in useEffect cleanup |
| Event listeners | addEventListener without removal | Return cleanup from useEffect |
| WebSocket connections | Stay open after navigation | Close on unmount |
| AbortController missing | Fetch completes after unmount, sets state | Abort in cleanup function |
| Closures over stale state | Hold references to unmounted component tree | Use refs or proper dependency arrays |
| Large object refs | Stored in state or context, never released | Null out on unmount |
| Third-party libraries | Map instances, chart objects, editors | Call .destroy() or .dispose() in cleanup |
Detection Checklist
| Method | What It Shows | When |
|---|---|---|
| Chrome DevTools Memory tab | Heap snapshots, allocation timeline | Suspected leak |
| React DevTools Profiler | Unnecessary re-renders holding refs | Performance degradation |
why-did-you-render | Identifies wasted renders | Development |
Node --inspect + heap dump | Server-side memory in API routes | Server memory growth |
| Vercel/hosting metrics | Memory usage over time | Production monitoring |
Prevention Pattern
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal });
return () => controller.abort(); // Always clean up
}, [url]);
Every useEffect with a side effect needs a cleanup return. No exceptions.
Context
- Performance — Memory and performance are the same problem
- Anti-patterns — Memory leaks are the silent anti-pattern
- Hooks — useEffect cleanup is the primary defense
- State Management — Global state that never releases is a leak
Questions
How do you detect a memory leak before users report crashes?
- What is the cost of a memory leak that only manifests after 30 minutes of use?
- When does server-side memory management differ from client-side in Next.js?
- How do third-party map and chart libraries change your cleanup obligations?