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, and 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 is 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 for Chrome and 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 and state bugs, open the Components panel, click the component, and see current state on the right. Change state directly to test behavior without modifying code.

Tip: In the Components panel, right-click a component and select "Log this component's data to the console". This gives you a full snapshot of props and state as a JS object, which is easier to inspect for 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 with npm run dev, then press F5 in VS Code. Chrome launches controlled by VS Code. Set breakpoints in .jsx or .tsx files and 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 causes infinite loop
useEffect(() => {
  fetchData()
}, [{ id: userId }])

Fix: Always include a dependency array. Primitives like string, number, and boolean are safe. Objects and arrays need useMemo or should be moved inside the effect.

javascript
useEffect(() => {
  if (userId) fetchUser(userId)
}, [userId])

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

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

Remove debugger before committing. It is 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
const [count, setCount] = useState(0)

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

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

javascript
setCount(prev => prev + 1)

State Mutation

javascript
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
const addItem = (item) => {
  setItems(prev => [...prev, item])
}

Step 6: Debug Network Requests

Open DevTools, go to the Network tab, and 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 the Authorization header present and in the correct format?
  • Request body: is the payload what you expected?

Tip: Click a request and select "Copy as cURL", then paste it in the terminal. This reproduces the exact request outside the browser. If it works in the terminal but fails in the browser, the issue is CORS or an auth header.

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 the 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 does not updateState mutationReturn new array or 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 or more 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

GitHub Copilot Just Changed Its Pricing. What Developers Need to Know

5 min read

Engineering

Why Your AI Agent Harness Fails at Debugging (And How to Fix It)

5 min read

← All posts