Use console.table to log tabular data

If the variable you are logging is an array of objects, instead of using traditional console.log() , make use of console.table() that gives you a nice tabular representation of the data that is much easier to consume.

Use colors to make your logs stand out

It's easy to get a lot of noise in the logs. It may be your own logs, logs from third-party widgets or the browser’s logs. Besides using filters, you can make your logs stand out by using colors.

$ and $$

If you are in the console and you are not using any library that assigns the $ and $$ global variables like jQuery, you can use the $ and $$ as shortcuts for document.querySelector() and document.querySelectorAll() respectively.

Besides saving keystrokes, $$ also returns a true array instead of an array-like NodeList. So you can use array methods like map, reduce and filter on the result immediately.

E.g. you can check for a broken link in your page using $$ like below

$0…$4

If all you want is a reference to a DOM element, use $0 shortcut. $0 gives you the reference to the currently selected element in the Elements panel. If $0 is the currently selected element, $1 is the previously selected element. That goes all the way to $4 .

$_

Use $_ to get hold of the result of last evaluated expression in the console.

getEventListeners()

getEventListeners(domElement) gives you an object with all the registered event listeners on that DOM element. E.g. if you want a reference to a click handler on a button, remove it or modify it in the runtime in the production environment, this allows you to do so.

You may have noticed in the GIF above, how the expression that was typed in the console was immediately being evaluated. I did not have to press enter to get the value, the value was eagerly displayed in the console. That’s a new feature available on Canary right now called “Eager evaluation”.

debug(fn)

If in the example above, all you want is to pause execution when the button is clicked, you can use the debug method. debug(fn) takes in a function reference as an argument and everytime that function is invoked, the debugger will then pause the execution on the first line of that function.

Imagine you have to debug an issue with a button click. You have no idea where in the source code lies the handler for the button. Instead of digging through the source code, use the getEventListeners method as displayed above to get the reference to the click handler of the button and pass that function reference to the debug method. Then, if you click the button, devtools will pause the execution on the first line of the click handler.

copy(obj)

copy(anything) is a handy utility in the console that allows you to put anything into the system clipboard.

Give copy some mangled JSON. It will pretty print it for you.

Top-level await

async/await makes handling async operations super easy and understandable. The only downside, await -ing needs to be done in an async function. If we want to use async/await in the devtools console, we would have to wrap it in an Immediately Invoked Async Function Expression (IIAFE). Not convenient. Which is why devtools now supports top-level await so there is no needs to wrap your await s inside an IIAFE.

Debugging in the Sources panel

Sources panel is where the true debugging happens. Using breakpoints, stepping-into, stepping-over etc. you can get a lot of info on the state of things in your application that can guide you towards the source of an issue. I am not going to write about how to use the debugger, and going to assume that you already have that knowledge. Instead, I am going to share some other tips and tricks that I like to use in the Sources panel.

Enable auto-pretty print

Now on Canary, behind an experimental flag, you can make sure that all the code that you see in the sources panel is automatically pretty-printed. So that’s one less thing you have to worry about.

Inject console logs in prod with conditional breakpoints

Breakpoints are great. Even better, conditional breakpoints! Conditional breakpoints are the breakpoints that only pause execution if a condition evaluates to true. So devtools won’t pause on you every single time, but only when you want it to. Learn more on conditional breakpoints here.

I like to use conditional breakpoints to inject console.logs in production code when I cannot modify the source code. If my breakpoint condition is just a console.log, devtools will never break on that breakpoint because console.log returns undefined, which is falsy. But it will evaluate my expression and therefore log my variable in the console.

Why not just use a regular breakpoint and inspect the variable? Sometimes, I don’t want that. E.g. when trying to inspect code that gets executed in high frequency like a touch or a scroll handler. I don’t want the debugger to pause every single time, but I do want to log my variables. Now, I can just inject my console.log even in production code.

Freeze UI to inspect elements that appear only on hover

It’s hard to inspect an element that appears only on hover. E.g. how do you inspect a tooltip? By the time you right-click and inspect, the element is no longer being hovered and the tooltip is gone. Is there a way to freeze the UI as is and then inspect any element on the page.

The trick I like to use is

Open up the sources panel Display the tooltip Use the keyboard shortcut to pause script execution. (Hover over the pause icon to find out the keyboard shortcut) When the script execution is paused, go back to the Elements panel and inspect the tooltip as you are used to

XHR breakpoints

When trying to figure out why a certain request is made and who made it, use the XHR breakpoint in the sources panel.

Use devtools as your IDE via Workspace

DevTools sources panel is a pretty advanced editor. You can easily fuzzy-search for a file, go to a line, go to a symbol, run a snippet, use multiple cursors etc. This has been documented well in this medium post by Addy Osmani.

But, why not bring the entire development workflow into the devtools so you don’t waste time switching applications and contexts. You can do so using Workspace.

E.g. if you have a project that you built with create-react-app or vue-cli, you can just drag the entire folder into the Sources panel. Devtools will automagically map the network resources into the local ones. So you can start editing the files in the devtools and view the changes right away. This makes for a very efficient feedback loop while debugging applications.

Use network overrides to tinker with the production code

If you are battling with a production bug and you just want to tinker with the production code without having to set things up locally, use the “network overrides” feature.

It allows you to easily map any resource that comes down over the network to a local copy which you can then edit in the devtools or your own editor at your own will. And devtools will always serve that local copy. Even when the cache is cleared or the resource changes on the server or when the service worker is involved.

This allows for some super easy debugging on production or some nifty performance experiments.

Nodejs debugging

If you want to use the same devtools debugger to debug your node applications, you can easily do so by invoking your node script with --inspect-brk flag.

node --inspect-brk script.js

Go to chrome://inspect. Your node session should appear as one of the remote targets that you can inspect.

Also, in devtools you will see a green node icon, clicking on which will fire up the chrome debugger for the node session.

If you wanted to debug your unit tests e.g. with devtools debugger, you’d have to do something like

node --inspect-brk ./node_modules/.bin/jest

It is kind of awkward having to figure out the link to the actual executable. GoogleChromeLabs recently released a tool to make this infinitely easier. It’s called ndb.

With ndb, all you need to do is

ndb npx jest

If you have a custom npm script, you can use devtools debugger for that too very easily.

ndb npm run unit

Even better, if you run ndb in a project with package.json, it will automatically find all the scripts from the package.json which you can then debug using devtools.

Use snippets for debugging utilities

Devtools come with a tool to create and save snippets of code that you can run anywhere. I like to use snippets to extend the debugger with some additional utility methods like

lodashify — to add lodash to any app so I have the entire Lodash utility belt with me all the time

Another example is a utility method that I have that can augment any object property so that whenever it is accessed or modified, it gives me a nice trace of who accessed that property or who changed it. Very useful when debugging state that was changed by something in the code somewhere.

There are lots of useful devtools snippets available that you can just grab and use to your advantage.

That’s it for now. This post will be followed up by another post with a focus on how to use devtools to debug performance issues. If you have additional tips and tricks, feel free to share in the comments section below. If you found it useful, please share it so others can benefit too.