DEV Community

Alex Chen
Alex Chen

Posted on

How to Debug JavaScript Like a Pro

How to Debug JavaScript Like a Pro

Console.log is fine. But these techniques are better.

Level 1: Console Methods Beyond log

// console.table — Beautiful data display
const users = [
  { name: 'Alex', age: 30, role: 'dev' },
  { name: 'Sam', age: 25, role: 'design' },
  { name: 'Charlie', age: 35, role: 'dev' },
];
console.table(users);

// console.group — Group related logs
console.group('API Request');
console.log('URL:', url);
console.log('Method:', method);
console.log('Headers:', headers);
console.groupEnd();

// console.time — Measure performance
console.time('fetchData');
await fetchData();
console.timeEnd('fetchData'); // fetchData: 1.234s

// console.assert — Silent unless false
console.assert(user.age >= 18, 'User is underage!', user);

// console.count — Count how many times called
function handleData(data) {
  console.count('handleData called');
}
// handleData called: 1
// handleData called: 2

// console.trace — Show call stack
function deepFunction() {
  console.trace('Here!');
}

// console.dir — Object properties (non-JSON-safe)
console.dir(document.body);

// Styled output
console.log(
  '%c ERROR! %c Something went wrong',
  'color: red; font-weight: bold; font-size: 14px',
  'color: gray'
);

// String substitution
const name = 'Alex';
const count = 42;
console.log('User %s has %d items', name, count); // User Alex has 42 items
Enter fullscreen mode Exit fullscreen mode

Level 2: Chrome DevTools Breakpoints

// Don't use debugger; statement in production!
// But in DevTools Sources panel:

// Regular breakpoint — click line number
// Conditional breakpoint — right-click → "Add conditional breakpoint"
// → Enter condition: data.length > 1000
// → Only pauses when condition is true

// Logpoint — right-click → "Add logpoint"
// → Enter: 'data.length:', data.length
// → Logs without pausing execution!

// DOM breakpoint — Elements panel → right-click element
// → Break on: subtree modifications, attribute changes, node removal

// XHR/Fetch breakpoint — Sources panel → XHR breakpoints
// → Add URL pattern containing: /api/users
// → Pauses on any matching request
Enter fullscreen mode Exit fullscreen mode

Level 3: Network Tab Mastery

Network tab columns:
┌──────────┬──────────┬───────┬──────────┬────────┐
│ Name     │ Status   │ Type  │ Time     │ Size   │
├──────────┼──────────┼───────┼──────────┼────────┤
│ api/user │ 200      │ xhr   │ 234ms    │ 1.2 KB │
│ app.js   │ 304      │ js    │ 45ms     │ 45 KB  │
│ style.css│ 200      │ css   │ 12ms     │ 2.1 KB │
└──────────┴──────────┴───────┴──────────┴────────┘

Key features:
- Right-click request → Copy as fetch/curl
- Preview tab: JSON formatted
- Response tab: Raw response
- Timing tab: Waterfall breakdown (DNS, TCP, TTFB, Download)
- Disable cache checkbox (top of network tab)
- Filter by type: Fetch/XHR/JS/CSS/Img/Font
- Preserve log (don't clear on navigation)
- Slow 3G / Fast 3G throttling
Enter fullscreen mode Exit fullscreen mode

Level 4: Source Maps & Pretty Print

// Minified code? Click {} button in Sources tab
// → Pretty prints minified code for readability

// Source maps enabled?
// Check Sources panel → Page tree → look for original .ts/.vue files
// If not showing:
// → Check webpack/vite config for devtool: 'source-map'
// → Verify .map file exists alongside .js file

// Blackbox scripts you don't care about:
// Settings → Blackboxing → Add pattern: node_modules/*
// → Step through your code, skip library code
Enter fullscreen mode Exit fullscreen mode

Level 5: Performance Profiling

// Performance tab: Record → Interact → Stop → Analyze

// Find slow functions:
// Call tree view → Sort by "Self time" (not total)
// → Functions with high self-time = optimization targets

// Memory leaks:
// Memory tab → Take heap snapshot
// Do action → Take another snapshot
// Compare snapshots → Look for detached DOM nodes + growing arrays

// Render performance:
// Rendering tab → Enable "Paint flashing"
// → Green flash = repaint (expensive if frequent)

// JS profiling in code:
performance.mark('startProcess');
doExpensiveWork();
performance.mark('endProcess');
performance.measure('process', 'startProcess', 'endProcess');

const measures = performance.getEntriesByName('process');
console.log(measures[0].duration); // ms
Enter fullscreen mode Exit fullscreen mode

Level 6: Common Bugs & How to Find Them

"TypeError: Cannot read property X of undefined"

// The bug
const user = apiResponse.data.user;
user.name; // Crash if data or user is undefined

// Quick debug: Log each level
console.log({ 
  hasResponse: !!apiResponse,
  hasData: !!apiResponse?.data,
  hasUser: !!apiResponse?.data?.user,
});

// Fix: Optional chaining + nullish coalescing
const name = apiResponse?.data?.user?.name ?? 'Anonymous';
Enter fullscreen mode Exit fullscreen mode

"My async function returns undefined"

// The bug
async function getData() {
  const res = await fetch('/api/data');
  res.json(); // Missing return!
}

// Fix
async function getData() {
  const res = await fetch('/api/data');
  return await res.json(); // ← Return the promise result
}

// Or even simpler
async function getData() {
  const res = await fetch('/api/data');
  return res.json(); // No need for double-await
}
Enter fullscreen mode Exit fullscreen mode

"State not updating in React"

// The bug
function handleClick() {
  setCount(count + 1);
  console.log(count); // Shows OLD value (batched!)
}

// Fix: Use functional update
function handleClick() {
  setCount(prev => prev + 1);
}

// Or check effect
useEffect(() => {
  console.log(count); // Shows NEW value
}, [count]);
Enter fullscreen mode Exit fullscreen mode

"This is undefined in callback"

// The bug
class MyClass {
  value = 42;

  print() {
    setTimeout(function() {
      console.log(this.value); // undefined!
    }, 100);
  }
}

// Fix 1: Arrow function (inherits this)
setTimeout(() => {
  console.log(this.value); // Works!
}, 100);

// Fix 2: Bind
setTimeout(this.print.bind(this), 100);

// Fix 3: Class field arrow
print = () => {
  console.log(this.value);
};
Enter fullscreen mode Exit fullscreen mode

What's your favorite debugging technique? Share below!

Follow @armorbreak for more JavaScript content.

Top comments (0)