On this page

Engineering8 min read

How to Debug a React Application in VS Code (Complete Guide)

Step-by-step guide to debugging React apps in VS Code — component errors, hooks bugs, state issues, and network failures — using DevTools and the VS Code debugger

ReactdebuggingVS CodeJavaScripttutorial

React Debugging Happens in Two Places

React apps have two debugging environments: browser DevTools (for component state, network, runtime errors) and VS Code (for breakpoints and step-through debugging). Both are useful for different problems.

Use browser DevTools for:

  • Reading error messages and component stacks
  • Inspecting state and props in React DevTools
  • Watching network requests
  • Console logging

Use VS Code debugger for:

  • Setting breakpoints in event handlers and useEffect
  • Stepping through complex logic line by line
  • Inspecting variable state without console.log spam

Step 1: Read the Error Overlay

React dev mode shows a red error overlay with the component stack when an unhandled error occurs. The component stack reads bottom-up:

TypeError: Cannot read properties of undefined (reading 'name')

The above error occurred in the <ProductCard> component:
    at ProductCard (src/components/ProductCard.jsx:18)
    at div
    at ProductList (src/pages/ProductList.jsx:34)
    at App

Start from ProductCard at line 18. Go to that line — that's where the error fires. The parent (ProductList line 34) is where ProductCard received the undefined prop.

Step 2: Install React DevTools

React DevTools is a browser extension (Chrome + Firefox) that adds two panels to DevTools:

  • Components — see the full component tree, inspect each component's props and state in real time
  • Profiler — record a session and see which components re-rendered and how long they took

For props/state bugs: open Components panel → click the component → see current state on the right. Change state directly to test behavior without modifying code.

Tip: In the Components panel, right-click a component → "Log this component's data to the console". Gets you a full snapshot of props and state as a JS object — easier to inspect complex nested state.

Step 3: Set Up VS Code Debugger for React

Create .vscode/launch.json in your project root:

json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug React (Chrome)",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}/src",
      "sourceMapPathOverrides": {
        "webpack:///src/*": "${webRoot}/*"
      }
    }
  ]
}

Start your dev server (npm run dev) then press F5 in VS Code. Chrome launches, controlled by VS Code. Set breakpoints in .jsx/.tsx files — execution pauses in VS Code when that line runs.

Note: For Vite-based projects, the url port is 5173 not 3000. Check your terminal for the correct port.

Step 4: Debug useEffect

useEffect is the most common source of React bugs. Common patterns:

javascript
// ❌ Missing dependency array — runs after every render
useEffect(() => {
  setUser(processUser(data))
})

// ❌ Object in dependency array — new object ref each render = infinite loop
useEffect(() => {
  fetchData()
}, [{ id: userId }])

Fix: Always include dependency array. Primitives (string, number, boolean) are safe. Objects and arrays need useMemo or move inside the effect.

javascript
// ✅ Correct dependencies
useEffect(() => {
  if (userId) fetchUser(userId)
}, [userId])  // primitive — stable reference

Set a breakpoint inside useEffect to see how many times it runs:

javascript
useEffect(() => {
  debugger  // VS Code pauses here on every run — count executions
  fetchUser(userId)
}, [userId])

Remove debugger before commit. It's a fast way to check effect frequency without adding console.log.

Step 5: Debug State Issues

When state looks wrong, the problem is almost always one of three things:

Stale closure:

javascript
// ❌ handleClick captures count at creation time, not current time
const [count, setCount] = useState(0)

useEffect(() => {
  const interval = setInterval(() => {
    console.log(count)  // always logs 0 — stale closure
  }, 1000)
  return () => clearInterval(interval)
}, [])

Fix: Use functional update form, or include count in dependency array.

javascript
// ✅ Functional update — always uses current state
setCount(prev => prev + 1)

State mutation (React doesn't detect):

javascript
// ❌ Direct mutation — React sees same object reference, doesn't re-render
const [items, setItems] = useState([])

const addItem = (item) => {
  items.push(item)  // mutates existing array
  setItems(items)   // same reference → no re-render
}

Fix: Always return new references.

javascript
// ✅ New array reference → React detects change
const addItem = (item) => {
  setItems(prev => [...prev, item])
}

Step 6: Debug Network Requests

Open DevTools → Network tab → filter by "Fetch/XHR". Every API call your React app makes appears here.

For each failed request, check:

  • Status code — 401 means auth, 422 means validation, 500 means server error
  • Response body — the actual error message from the server
  • Request headers — is Authorization header present? Correct format?
  • Request body — is the payload what you expected?

Tip: Click a request → "Copy as cURL" → paste in terminal. Reproduces the exact request outside the browser. If it works in terminal but fails in browser, CORS or auth header issue.

Step 7: Debug Custom Hooks

Custom hooks are functions — debug them like functions. Add a breakpoint at the first line of the hook, or return debug info alongside the main return value:

javascript
function useProductData(productId) {
  const [data, setData] = useState(null)
  const [error, setError] = useState(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetchProduct(productId)
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false))
  }, [productId])

  // Temporary debug — remove before shipping
  console.log('useProductData:', { productId, data, error, loading })

  return { data, error, loading }
}

In React DevTools Components panel, find the component using the hook — its state shows the hook's state values directly.

Common React Errors Quick Reference

ErrorLikely causeFirst check
Cannot read properties of undefinedProp is undefined, accessing property too earlyCheck parent passes prop, add optional chaining
Each child in a list should have a unique keyList rendered without stable keyAdd key={item.id} (not array index)
Infinite re-renderuseEffect dependency causes re-render which triggers effectCheck effect dependencies, memoize objects
Stale data in handlerStale closure capturing old stateUse functional update setState(prev => ...)
Component doesn't updateState mutationReturn new array/object instead of mutating
Rendered more hooks than previous renderHooks called conditionallyMove hook calls before any early returns

For cross-component state bugs — where state passes through 3+ components and arrives wrong at the bottom — paste the component tree into DebugAI. It reads the prop-drilling chain and identifies where the value changes or gets lost.

Debug faster starting today.

Free VS Code extension. 10 sessions/day. No credit card.

Install Free →

Related Posts

Engineering

Codebase-Aware AI Debugging vs Generic AI: Why Context Changes Everything

7 min read

Engineering

Fix Python Circular Import Error — What It Is and How to Resolve It

6 min read

← All posts