Most developers are comfortable using the standard Network and Elements tabs, but there comes a point in every complex project where the built-in tools aren’t enough. Whether you’re managing a complex state machine or debugging a proprietary protocol, knowing how to create a Chrome DevTools extension allows you to bring your internal application data directly into the browser’s native debugging environment.
In my experience building internal tooling for large-scale React apps, I’ve found that custom panels reduce debugging time by 30-40% because they eliminate the need to constantly console.log large objects. Instead of guessing, you can visualize your data in real-time.
The Challenge: The DevTools Architecture Gap
Creating a DevTools extension is fundamentally different from creating a standard Chrome extension. While a typical extension might use a popup or an options page, a DevTools extension operates across three distinct layers: the Extension Background, the DevTools Page, and the DevTools Panel.
The main challenge is that the DevTools panel lives in an isolated environment. It cannot directly access the DOM of the page you are inspecting. To bridge this gap, you must implement a messaging system that routes data from the inspected page, through the DevTools page, and finally into your custom UI panel.
Solution Overview: The Communication Pipeline
To successfully implement this, we use a ‘bridge’ pattern. Here is the high-level flow:
- Inspected Window: Where your app runs. It sends data via
window.postMessageor a content script. - DevTools Panel: The UI where you visualize data. It requests data from the inspected window.
- DevTools Page: The invisible coordinator that initializes the panel and manages the connection.
If you’re coming from a background of using other tools, you might find this similar to how the React Developer Tools extension guide describes the bridge between the React internals and the browser UI.
Implementation: Building Your First Panel
Step 1: The Manifest File
Your manifest.json must explicitly define the devtools_page. This is the entry point that Chrome looks for when the DevTools window opens.
{
"manifest_version": 3,
"name": "Custom Debugger",
"version": "1.0",
"devtools_page": "devtools.html",
"permissions": ["tabs", "storage"]
}
Step 2: The DevTools Coordinator
The devtools.html file is not a visible page; it’s a script loader. It should simply link to a JavaScript file that creates the panel.
// devtools.js
chrome.devtools.panels.create(
"My Debugger", // Panel title
"icon.png", // Panel icon
"panel.html", // The HTML file for the panel UI
function(panel) {
console.log("Custom panel created!");
}
);
Step 3: The Panel UI and Data Fetching
In your panel.html and associated JS, you can now use the chrome.devtools.inspectedWindow API. This is the secret sauce for interacting with the page being debugged.
// panel.js
async function getAppData() {
// This executes code directly in the context of the inspected page
const result = await chrome.devtools.inspectedWindow.eval("window.myAppState");
document.getElementById('output').innerText = JSON.stringify(result, null, 2);
}
setInterval(getAppData, 1000); // Poll for state changes
As shown in the diagram above, this eval call bypasses the standard content script limitations, allowing you to pull variables directly from the page’s global window object.
Case Study: Debugging a WebSocket State Machine
I recently used this approach to debug a real-time trading dashboard. The app had a complex internal state for WebSocket connections that was invisible to the Network tab. By creating a custom DevTools panel, I was able to:
- Visualize the current connection state (Connecting, Open, Closing).
- Trigger manual ‘reconnect’ events via
inspectedWindow.eval(). - Log incoming binary packets into a human-readable table.
This was significantly faster than adding a hidden HTML overlay to the app itself, as it kept the production UI clean while providing deep technical insights.
Common Pitfalls and Pitfalls
During my testing, I encountered a few recurring issues that you should watch out for:
- Context Isolation: Remember that
chrome.devtools.inspectedWindow.evalruns in the page context, but your panel JS runs in the extension context. You cannot pass complex functions or class instances back and forth—only JSON-serializable data. - Performance Overhead: Polling the inspected window every 100ms can lag the browser. I recommend using a
window.postMessageevent from the app to the extension for push-based updates. - Manifest V3 Permissions: Be strict with your permissions. If you don’t need
tabsaccess, remove it to ensure a faster review process if you plan to publish to the Web Store.
For those looking to improve their general debugging workflow before diving into custom extensions, I recommend checking out my list of the best devtools extensions for debugging JavaScript to see what patterns already exist.
Summary: When to Build Your Own
Building a custom extension is a high-effort, high-reward task. If you only need to see a few variables, stick to console.log or the standard Sources tab. However, if you are building a framework, a complex library, or a highly state-dependent application, a custom DevTools panel is the gold standard for developer experience (DX).