Beyond console.log: The Console API
The browser console is far richer than a single console.log. These methods give you structured output, conditional logging, and styling — all without touching production code.
// console.table — renders arrays/objects as a table
const users = [
{ name: "Alice", age: 30, role: "admin" },
{ name: "Bob", age: 25, role: "user" },
];
console.table(users);
// Optional second arg: restrict which columns to show
console.table(users, ["name", "role"]);Breakpoints — Pause Without Changing Code
Breakpoints let you freeze execution at any point and inspect the live program state. DevTools offers several types — each suited to a different debugging scenario.
// Logpoint — right-click a line gutter in Sources -> Add logpoint
// Type an expression; DevTools prints it without changing your source.
// Example logpoint expression:
"fetchUser called with id =", userId, "at", new Date().toISOString()
// No console.log in your code — logs disappear when you remove the logpoint.Stepping keyboard shortcuts — memorise these to move through paused code without touching the mouse:
Sources Panel: Call Stack, Scope, and Watch
When execution is paused, the Sources panel becomes your primary inspection tool. Each pane reveals a different facet of runtime state.
Performance Panel: Flame Charts and Long Tasks
The Performance panel records a detailed timeline of everything the browser does — JavaScript execution, style recalculation, layout, paint, and compositing. It is the primary tool for diagnosing jank and slow interactions.
Network Panel: Waterfall and Timing
The Network panel records every request made by the page. The waterfall view shows when each request started relative to others and how long each phase took — essential for diagnosing slow page loads.
| Timing Phase | What It Measures |
|---|---|
| Queueing | Time waiting before the browser could send the request (connection limits, priorities) |
| Stalled | Time blocked after queueing — e.g. waiting for a free TCP connection |
| DNS Lookup | Time to resolve the hostname to an IP address |
| Initial connection | TCP handshake (+ TLS negotiation for HTTPS) |
| TTFB (Waiting) | Time to First Byte — server processing + network round-trip |
| Content Download | Time to receive the full response body |
Coverage Tab: Find Unused JS and CSS
The Coverage tab (open via Ctrl Shift P → "Show Coverage") records which bytes of every JS and CSS file were actually executed during a recording session. Red bars = unused code.
import() dynamic imports so they load only when needed.User Timing API and Source Maps
Instrument your own code with performance.mark and performance.measure to add named markers that appear directly in the Performance panel timeline — no guessing which flame chart entry corresponds to your feature.
// User Timing API — custom marks visible in the Performance panel
performance.mark("data-fetch-start");
const data = await fetchDashboard();
performance.mark("data-fetch-end");
performance.measure("data-fetch", "data-fetch-start", "data-fetch-end");
// Read timings programmatically
const [entry] = performance.getEntriesByName("data-fetch");
console.log(`fetch took ${entry.duration.toFixed(1)} ms`);
// Clean up after reading
performance.clearMarks();
performance.clearMeasures();// tsconfig.json — enable source maps
{
"compilerOptions": { "sourceMap": true }
}
// vite.config.ts
export default defineConfig({
build: { sourcemap: true }, // or "inline", "hidden"
});
// Once deployed, DevTools automatically maps minified code back to
// your original TypeScript source — breakpoints, stack traces, and
// variable names all reflect your source files.