| name | react-errors-debugging |
| description | Use when debugging React applications, interpreting console warnings, profiling performance, or understanding Strict Mode double-rendering. Prevents the common mistake of ignoring React warnings or misdiagnosing Strict Mode behavior as bugs. Covers React DevTools, Strict Mode, console warnings, Profiler, component stack traces, development vs production error differences. Keywords: DevTools, Strict Mode, Profiler, console warnings, stack traces, white screen, blank page, nothing renders, component not showing, React DevTools, double render..
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires React 18.x or 19.x with TypeScript. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
react-errors-debugging
Quick Reference
Debugging Tools Overview
| Tool | Purpose | When to Use |
|---|
| React DevTools (Components) | Inspect props, state, hooks, context | Component data inspection |
| React DevTools (Profiler) | Render timing, flamegraph, ranked chart | Performance investigation |
| Browser DevTools (Console) | Read React warnings and error messages | Error diagnosis |
| Browser DevTools (Sources) | Set breakpoints, step through code | Logic debugging |
<StrictMode> | Surface impure renders, unsafe effects | Development-time bug prevention |
why-did-you-render | Log unnecessary re-renders with reasons | Re-render optimization |
| Source maps | Map production errors to original source | Production debugging |
Critical Warnings
NEVER remove <StrictMode> to "fix" double-rendering issues -- the double invocation is intentional. It exposes impure components and side effects. ALWAYS fix the underlying impurity instead.
NEVER ignore React console warnings -- they indicate real bugs or future breaking changes. ALWAYS resolve every warning before shipping.
NEVER debug production builds without source maps -- error messages are stripped and minified in production. ALWAYS generate source maps for staging/debugging environments.
ALWAYS check the component stack trace when diagnosing errors -- React prints the component tree path that led to the error, which pinpoints the source.
ALWAYS use the Profiler tab (not just the Components tab) when investigating performance -- visual inspection of the component tree does NOT reveal render timing.
Strict Mode
<StrictMode> is a development-only tool that helps find bugs by intentionally double-invoking certain functions.
What Strict Mode Double-Invokes
| Function Type | Double-Invoked? | Why |
|---|
| Component function body | Yes | Detects impure rendering (side effects during render) |
useState initializer | Yes | Detects impure initialization |
useReducer reducer | Yes | Detects impure reducers |
useReducer initializer | Yes | Detects impure initialization |
useMemo callback | Yes | Detects impure memoization |
useEffect setup + cleanup | Yes (mount/unmount/remount) | Detects missing cleanup logic |
useRef — NO | No | Refs are not double-invoked |
Strict Mode Behavior
import { StrictMode } from "react";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
In development, React will:
- Render component -> call cleanup -> re-render component (simulating unmount/remount)
- Call state initializers twice and discard the first result
- Call reducers twice and discard the first result
Common Strict Mode Mistakes
function Counter(): JSX.Element {
const [count, setCount] = useState(0);
document.title = `Count: ${count}`;
return <div>{count}</div>;
}
function Counter(): JSX.Element {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return <div>{count}</div>;
}
function ChatRoom({ roomId }: { roomId: string }): JSX.Element {
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
}, [roomId]);
return <div>Chat: {roomId}</div>;
}
function ChatRoom({ roomId }: { roomId: string }): JSX.Element {
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]);
return <div>Chat: {roomId}</div>;
}
Console Warnings Diagnostic Table
| Warning Message | Cause | Fix |
|---|
| "Each child in a list should have a unique 'key' prop" | Array items rendered without key or with duplicate/index keys | ALWAYS provide a stable, unique key from data (e.g., id). NEVER use array index as key when items reorder. |
"Cannot update a component (X) while rendering a different component (Y)" | Calling setState of component X inside the render body of component Y | Move the state update into a useEffect or event handler. NEVER call setState during render of another component. |
| "Maximum update depth exceeded" | Infinite re-render loop from setState in useEffect without proper deps | Check useEffect dependencies — ensure the effect does not unconditionally trigger its own dependency. Add missing deps or restructure logic. |
| "Objects are not valid as a React child" | Passing an object/array directly as JSX child text | Convert to string with JSON.stringify(), or map array to elements. NEVER render raw objects. |
| "Function components cannot be given refs" | Passing ref to a function component without forwardRef | Wrap component with forwardRef (React 18) or accept ref as a prop (React 19). |
| "Can't perform a React state update on an unmounted component" | setState called after component unmounted (React 17 and earlier) | This warning was REMOVED in React 18. If seen in React 18+, you have a version mismatch. In React 17, use cleanup to cancel async operations. |
| "Invalid hook call" | Hook called outside component/hook, or mismatched React versions | Verify hooks are at top level of component/hook. Check for duplicate React installations with npm ls react. |
| "Rendered more hooks than during the previous render" | Conditional hook call — hook count changed between renders | NEVER call hooks inside conditions, loops, or early returns. ALWAYS call all hooks in the same order. |
React DevTools
Components Tab
The Components tab shows the live component tree with inspectable data:
| Feature | What It Shows | How to Use |
|---|
| Component tree | Hierarchy of mounted components | Click to select, use search to filter |
| Props panel | Current props of selected component | Expand objects, edit values live |
| Hooks panel | Current hook values (state, ref, memo, context) | Shows hook name and current value |
| Source link | "< >" icon links to component source | Click to open in Sources tab |
| Rendered by | Which parent rendered this component | Shows in sidebar when component selected |
| Owner stack (React 19) | Full owner component chain | Shows which component created this one |
Profiler Tab
| Feature | Purpose | Interpretation |
|---|
| Flamegraph | Visualize render time per component | Wide bars = slow renders. Grey bars = did not render. |
| Ranked chart | Components sorted by render duration | Focus optimization on top-ranked components |
| "Why did this render?" | Shows render trigger reason | Enable via gear icon -> "Record why each component rendered" |
| Commit selector | Browse individual render commits | Each commit = one React render batch |
| Render duration | Time in ms for each component | Compare before/after optimization |
Profiler Workflow
- Open React DevTools -> Profiler tab
- Click gear icon -> enable "Record why each component rendered while profiling"
- Click Record (blue circle)
- Perform the interaction you want to measure
- Click Stop
- Analyze: flamegraph for overview, ranked chart for worst offenders
- Check "Why did this render?" for each slow component
Component Stack Traces
When React throws an error, it prints a component stack trace showing the component hierarchy:
Error: Something went wrong
at UserProfile (UserProfile.tsx:15)
at div
at Dashboard (Dashboard.tsx:8)
at ErrorBoundary (ErrorBoundary.tsx:5)
at App (App.tsx:12)
Reading Component Stacks
- Read top to bottom: the topmost component is where the error occurred
- Native HTML elements (e.g.,
at div) appear in the stack
- File names and line numbers point to the component definition
- ALWAYS look at the first custom component in the stack -- that is the error source
React 19 Improvements
React 19 introduces owner stacks via captureOwnerStack():
import { captureOwnerStack } from "react";
function handleError(error: Error): void {
const ownerStack = captureOwnerStack();
console.error("Error:", error.message);
console.error("Owner stack:", ownerStack);
}
Common Error Messages Diagnostic Table
| Error Message | Cause | Fix |
|---|
Minified React error #XXX | Production build with stripped error messages | Look up the error code at https://react.dev/errors/XXX or debug with development build |
Too many re-renders | Component unconditionally calls setState during render | NEVER call setState directly in component body. Use useEffect or event handlers. |
Hydration failed because the initial UI does not match | Server HTML differs from client render | Ensure server and client render identical output. Check for typeof window guards, Date.now(), or random values in render. |
Cannot read properties of null (reading 'useState') | Multiple React copies or hook called outside component | Run npm ls react to check for duplicates. Ensure hooks are called inside components only. |
Uncaught Invariant Violation | Internal React assertion failed | Usually indicates a bug in your code violating React rules. Check the message details. |
Element type is invalid: expected a string or class/function | Component is undefined at render time | Check import statement — likely a typo, default vs named export mismatch, or circular dependency. |
Cannot update during an existing state transition | setState called inside render() or getDerivedStateFromProps | Move state update to useEffect or componentDidMount/componentDidUpdate. |
Development vs Production Differences
| Aspect | Development | Production |
|---|
| Error messages | Full descriptive messages with component stacks | Minified error codes (e.g., Minified React error #310) |
| Strict Mode | Active (double-invokes renders, effects) | Completely stripped — no double invocations |
| Console warnings | All warnings displayed | Most warnings suppressed |
| Performance | Slower due to extra checks, Strict Mode overhead | Optimized, no dev-only checks |
| Profiling | DevTools Profiler works out of the box | Requires profiling build: react-dom/profiling |
| Source maps | Available by default (dev server) | Must be explicitly generated and deployed |
| PropTypes | Validated at runtime (if used) | Stripped entirely |
Debugging Production Errors
- Look up minified error codes: Visit
https://react.dev/errors/{code} for the full message
- Generate source maps: Configure your bundler to produce
.map files for staging
- Use profiling build for production performance analysis:
resolve: {
alias: {
"react-dom$": "react-dom/profiling",
"scheduler/tracing": "scheduler/tracing-profiling",
},
}
- Error monitoring: Use services that can decode source maps server-side (Sentry, Bugsnag)
React 19 Debugging Improvements
| Feature | React 18 | React 19 |
|---|
| Hydration mismatch errors | Generic "did not match" message | Detailed diff showing exact HTML differences |
| Error reporting | Component stack only | Owner stacks via captureOwnerStack() |
| Third-party script handling | Hydration errors on injected elements | Automatically skips unexpected <script>, <style>, <link> in <head>/<body> |
| ref on function components | Requires forwardRef wrapper | ref is a regular prop — no wrapper needed |
| useEffect cleanup timing | Synchronous during unmount | Consistent async cleanup with transition support |
Debugging Tools Setup
React DevTools Extension
ALWAYS install the React DevTools browser extension for Chrome or Firefox. It adds the Components and Profiler tabs to browser DevTools.
- Chrome: Search "React Developer Tools" in Chrome Web Store
- Firefox: Search "React Developer Tools" in Firefox Add-ons
- Standalone (for React Native/iframe):
npx react-devtools
why-did-you-render Library
For granular re-render tracking beyond DevTools Profiler:
import React from "react";
if (process.env.NODE_ENV === "development") {
const whyDidYouRender = await import("@welldone-software/why-did-you-render");
whyDidYouRender.default(React, {
trackAllPureComponents: true,
});
}
MyComponent.whyDidYouRender = true;
Reference Links
Official Sources