The debugger statement is the fastest way to pause JavaScript execution at a specific line and inspect real component state. In React, that makes it useful when a bug only appears after a particular click, render, or effect runs and you want to stop exactly where the problem happens.
It is not a replacement for browser breakpoints or proper logging. It is a quick, local tool for narrowing the problem when a component behaves differently than you expected.
The short version
If you need to inspect props, state, or derived values during a render path, insert debugger at the suspicious line and open the browser DevTools. Execution will pause there as long as DevTools is open.
That is often faster than adding and removing console.log calls, especially in a component that only fails under one interaction path.
A basic example
Here is a simple component with a bug in its click flow.
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
debugger;
setCount((value) => value + 1);
}
return <button onClick={handleClick}>Count: {count}</button>;
}
When the button is clicked, the browser pauses on debugger. At that point you can inspect count, step into the handler, and see whether the state update happens when you expect.
Where it helps most in React
1 Event handlers
Pause inside a click, change, or submit handler to inspect the state right before it mutates.
2 Effects
Pause inside useEffect when a fetch, subscription, or derived value behaves unexpectedly.
3 Conditional branches
Pause only when a specific branch runs, so you do not stop on every render.
Debugging a render path
Sometimes the problem is not the event itself. It is the derived data that a component computes during render.
type User = {
name: string;
isAdmin: boolean;
};
type Props = {
user: User;
search: string;
};
export function UserBadge({ user, search }: Props) {
const visible = user.name.toLowerCase().includes(search.toLowerCase());
debugger;
if (!visible) {
return null;
}
return <span>{user.isAdmin ? 'Admin' : 'Member'}: {user.name}</span>;
}
That pause point lets you inspect the current props and the derived visible value before React decides what to render. It is a good fit when the output looks wrong but the bug is buried in a small chain of calculations.
debugger vs browser breakpoints
| Approach | Best use | Main tradeoff |
|---|---|---|
| debugger | Fast, temporary pause points inside the code | You must remove it after the bug is fixed |
| Browser breakpoint | Repeatable debugging while exploring the same line many times | Takes a little longer to set up |
| console.log | Quick tracing when you only need a few values | Harder to reason about once the logs pile up |
The practical rule is simple: use debugger when you want to stop now, use a breakpoint when you expect to return to the same line many times, and use logs when you only need lightweight tracing.
One React-specific caution
React Strict Mode can make debugging confusing because some development-only paths run more than once. If a debugger statement pauses more often than expected, check whether your component is inside Strict Mode and whether the extra pause is coming from a double render.
That does not make the statement less useful. It just means the pause is showing you the real development behavior, not a simplified mental model.
A better workflow
Bottom line
The debugger statement is a small tool, but in React it can save time when the bug only shows up in one interaction path. Drop it into the suspicious line, reproduce the issue, inspect the paused state, and remove it once you know what is happening.
Used that way, it is one of the quickest ways to turn a vague React bug into a concrete traceable state transition.