The fastest way to debug React applications is to follow the same loop every time: reproduce the failure, classify when it happens, read the message, isolate the cause, and verify the fix. This process turns a blank crash screen into one testable hypothesis instead of a guessing game.
Reproduce the failure first
A bug you cannot reproduce cannot be verified as fixed. Before editing code, capture the exact steps that trigger it. Which route, props, and user actions are involved?
Keep Strict Mode on during development. It re-renders components and re-runs Effects an extra time, which surfaces impure rendering and missing cleanup early. That double work is development only and never runs in production.
A reproducible crash often comes from a component that assumes its data is always present:
export default function Profile({ user }) {
return (
<article>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</article>
);
}When user is null, this throws Cannot read properties of null (reading 'name'). The message names the exact property and the component in the stack, which is already half the diagnosis. The fix is to guard the render or handle the missing data before it reaches this component.
export default function Profile({ user }) {
if (!user) {
return <p>No profile to show.</p>;
}
return (
<article>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</article>
);
}The guarded version renders a clear fallback instead of crashing. The visible result is a useful empty state on screen, not a removed tree.
Classify when it happens
React failures happen in distinct phases, and each phase points to a different fix. Naming the phase narrows the search before you read a single line of stack.
| Where the error happens | Typical trigger | First tool |
|---|---|---|
| During render | missing prop or bad data shape | error boundary plus the stack |
| In an event handler | click or submit logic | try/catch in the handler |
| In async work | fetch, timer, or promise | catch plus loading and error state |
| During hydration | server and client markup differ | the hydration warning in the console |
Render errors are the only kind an error boundary catches, which matters when you choose where to guard. See why error boundaries do not catch every error for the full list.
Read the message, then the stack
The error overlay shows the message first and the component stack second. The message tells you which property or method failed. The stack tells you which component threw it and how it was reached.
For a crash inside a child, the stack often reads in ComponentThatThrows (created by App). That line is your entry point: open that component in DevTools and inspect its props at the moment of failure.
The loop ends only when the original reproduction passes. If a fix does not hold, the diagram sends you back to classification, not back to a random new edit.
Isolate with React DevTools
Open the Components panel and select the throwing component. React DevTools shows its current props and state, which usually exposes the bad value immediately.
For problems that are slow instead of crashing, use the Profiler. Record the interaction and look for a component that re-renders far more than its data changes. The workflow is covered in how to find unnecessary re-renders with React Profiler.
Verify the fix
Re-run the exact reproduction steps after every change. Confirm the UI renders and the console stays clean. If you added a guard or an error boundary, trigger the failure again and confirm the fallback appears and the app keeps working.
A fix is only complete when the original repro passes and the surrounding UI still behaves.
What to learn next
Hydration failures follow their own rules because they compare server markup to client render. See hydration failed in React: causes and fixes when that is the phase you classified.
Rune AI
Key Insights
- Reproduce the failure before changing any code.
- Classify whether the error happens in render, an event, async work, or hydration.
- Read the error message first, then the component stack.
- Use DevTools to inspect props and state at the failure.
- Verify the fix against the original reproduction steps.
Frequently Asked Questions
Where should I start when a React app crashes?
Does React DevTools help with runtime errors?
Conclusion
A systematic debug loop beats random edits. Reproduce the failure, classify its phase, read the message and stack, isolate with DevTools, then verify the fix against the original repro.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.