# How WebSocket DevTools Integrates as a Custom Panel in Chrome DevTools

> Discover how law-chain-hot/websocket-devtools integrates as a custom panel in Chrome DevTools. Learn about its manifest registration, bootstrap script, and real-time WebSocket traffic capture.

- Repository: [Brian 阿布/websocket-devtools](https://github.com/law-chain-hot/websocket-devtools)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The `law-chain-hot/websocket-devtools` extension registers a DevTools page in its manifest, loads a bootstrap script that conditionally calls `chrome.devtools.panels.create()`, and renders a React UI that establishes a runtime port to capture WebSocket traffic in real time.**

The `law-chain-hot/websocket-devtools` repository demonstrates the standard pattern for integrating as a custom panel within the Chrome DevTools interface. By declaring a DevTools page in the extension manifest and leveraging the `chrome.devtools.panels` API, the extension injects a dedicated "WebSocket DevTools" tab that communicates with the inspected page through a background script bridge.

## Manifest Declaration for DevTools Pages

Chrome extensions must declare a DevTools page entry point to trigger custom panel registration. In [`src/manifest.json`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/manifest.json), the extension specifies the HTML file that Chrome loads automatically whenever DevTools opens for a tab.

```json
// src/manifest.json
"devtools_page": "src/devtools/devtools.html"

```

This declaration instructs Chrome to load [`src/devtools/devtools.html`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/devtools.html) in an invisible DevTools context, which then executes the bootstrap logic required to create the custom panel.

## Conditional Panel Registration

When DevTools opens, Chrome loads [`src/devtools/devtools.html`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/devtools.html), which executes [`src/devtools/devtools.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/devtools.js). This script verifies the extension's enabled state via storage before registering the panel, ensuring the UI only appears when the user has activated the extension.

```javascript
// src/devtools/devtools.js
chrome.storage.local.get(["websocket-proxy-enabled"], result => {
  const enabled = result["websocket-proxy-enabled"] !== false; // default enabled
  if (enabled) {
    chrome.devtools.panels.create(
      "WebSocket DevTools",                // panel title
      "icons/icon.svg",                    // panel icon
      "src/devtools/panel.html",           // panel UI
      panel => { /* optional callbacks */ }
    );
  }
});

```

The `chrome.devtools.panels.create()` method accepts four parameters: the display title, an icon path, the HTML page to load within the panel, and an optional callback that executes once the panel is created. This call adds the "WebSocket DevTools" tab to the DevTools UI immediately upon invocation.

## Bootstrapping the React Panel Interface

Once the panel is registered, Chrome loads [`src/devtools/panel.html`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.html) into the new tab. This file mounts a React application that handles the complex UI rendering and WebSocket inspection logic.

```html
<!-- src/devtools/panel.html -->
<script type="module" src="panel.jsx"></script>

```

The separation between [`devtools.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/devtools.js) (bootstrap) and [`panel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/panel.jsx) (UI) ensures that heavy React dependencies load only when the user actually opens the WebSocket DevTools tab, keeping DevTools startup performance optimal.

## Runtime Port Communication Architecture

Custom DevTools panels cannot directly access the inspected page due to Chrome's security sandboxing. Instead, [`src/devtools/panel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.jsx) establishes a persistent connection to the background script, which mediates all communication with the content script injected into the target tab.

### Panel-to-Background Handshake

Upon mounting, the React component opens a named runtime port and transmits the inspected tab's ID. This initialization sequence allows the background script to correlate incoming WebSocket events with the correct DevTools instance.

```javascript
// src/devtools/panel.jsx
const port = chrome.runtime.connect({ name: "devtools" });
port.postMessage({ type: "init", tabId });
window._wsInspectorPort = port;

```

The `chrome.runtime.connect()` call creates a long-lived port object. By storing this port on the window object, the component ensures that event handlers throughout the React application can reference the same communication channel to send commands or receive updates.

### Background Message Routing

The background script listens for these connections in [`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js). When it receives the initialization message containing the tab ID, it registers the port association and prepares to forward WebSocket events captured by the content script.

```javascript
// src/background/background.js
chrome.runtime.onConnect.addListener(port => {
  if (port.name === "devtools") {
    port.onMessage.addListener(msg => {
      if (msg.type === "init") {
        // store tabId, associate with this port, forward events later
      }
    });
  }
});

```

This architecture enables the panel to receive real-time WebSocket frame data, connection status changes, and control responses without polling or repeated message passing overhead.

## Content Script Data Capture

While the panel manages the display layer, [`src/content/content.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/content.js) performs the actual WebSocket interception. This script hooks the `WebSocket` constructor in the inspected page's main world, captures all message events, and transmits them to the background script. The background script then routes these events to the appropriate panel instance based on the tab ID established during the initialization handshake, completing the data flow from page to DevTools UI.

## Summary

- The extension declares `"devtools_page": "src/devtools/devtools.html"` in [`src/manifest.json`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/manifest.json) to trigger loading whenever Chrome DevTools opens.
- [`src/devtools/devtools.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/devtools.js) conditionally registers the custom panel using `chrome.devtools.panels.create()` after verifying the `websocket-proxy-enabled` storage setting.
- The panel UI renders via React in [`src/devtools/panel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.jsx), which is loaded by [`src/devtools/panel.html`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.html) only when the user activates the tab.
- `chrome.runtime.connect({ name: "devtools" })` establishes a persistent port between the panel and [`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js).
- The background script coordinates with [`src/content/content.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/content.js) to intercept WebSocket traffic and forward it to the correct panel instance based on the inspected tab ID.

## Frequently Asked Questions

### What manifest key is required to add a custom panel to Chrome DevTools?

Chrome extensions must include the `devtools_page` key in [`manifest.json`](https://github.com/law-chain-hot/websocket-devtools/blob/main/manifest.json), pointing to an HTML file that loads the bootstrap script. In `law-chain-hot/websocket-devtools`, this is set to [`src/devtools/devtools.html`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/devtools.html), which executes the panel registration logic when DevTools opens for any tab.

### How does the WebSocket DevTools panel communicate with the inspected page?

The panel does not communicate directly with the inspected page. Instead, it opens a runtime port to the background script using `chrome.runtime.connect({name: "devtools"})`. The background script then coordinates with a content script injected into the page, which intercepts WebSocket activity and forwards events back through the same port to update the panel UI in real time.

### Can the WebSocket DevTools panel be disabled without uninstalling the extension?

Yes. The extension checks `chrome.storage.local` for a `"websocket-proxy-enabled"` key before calling `chrome.devtools.panels.create()`. If the value is explicitly set to `false`, the panel registration is skipped, effectively disabling the custom tab while keeping the extension installed and its content scripts inactive.

### Which Chrome APIs are used to create and manage the custom DevTools panel?

The integration relies on three primary Chrome APIs: `chrome.devtools.panels.create()` to register the UI tab, `chrome.runtime.connect()` to establish persistent communication between the panel and background script, and `chrome.storage.local` to persist user preferences regarding panel visibility.