Devtron: an Electron DevTools tab that records the IPC traffic you cannot see
An Electron DevTools Extension
At a glance
- What is it?
- Devtron installs itself into an Electron app and shows up as a Developer Tools tab that lists IPC events, with getEvents() exposing the same list to your main process. It needs Electron 36 or newer, it misses events fired immediately at startup, and it splits ipcRenderer.once into two entries.
- Who is it for?
- Adopt Devtron if you maintain an Electron app whose main-to-renderer messaging has grown past what you can hold in your head, since a chronological IPC log in the DevTools panel is the cheapest possible substitute for logging every send and receive. Do not rely on it as a complete audit trail, because the README states that IPC sent immediately after startup may not be captured and that once-listeners are recorded as an on plus a removeListener pair.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One install call, and it appears as a DevTools tab
The mechanism is an extension loaded into your app rather than a separate tool you run against it. In your main entry point you import the package and call install.
// main.js
const { devtron } = require('@electron/devtron');
// or import { devtron } from '@electron/devtron'
devtron.install(); // call this function at the top of your fileThe README is emphatic about the placement, call this function at the top of your file, and the limitations section explains why: for Devtron to work with newly created sessions, you must call install() before those sessions are created. The ordering requirement is architectural, not stylistic.
The visible result is a tab in the Developer Tools of your Electron app. Nothing is printed to your console and no separate window opens; the data lands in the same panel your developers already have open.
If installation worked, that tab is there. If it is not, the README's requirement list is the diagnostic: Electron 36.0.0 or higher, install called early enough, and the app actually running the code you edited.
Gate the install so production builds carry nothing
The README's second example exists because shipping a devtools extension in a packaged app is not something you want. It uses the packaged flag as the switch and loads the module dynamically, so the import cost and the extension itself never happen in a release build.
const { app } = require('electron');
const isDev = !app.isPackaged;
async function installDevtron() {
const { devtron } = await import('@electron/devtron');
await devtron.install();
}
if (isDev) {
installDevtron().catch((error) => {
console.error('Failed to install Devtron:', error);
});
}Two details are worth copying deliberately. The import is dynamic, so the dependency is not evaluated at module load in a packaged app, and install() is awaited, with a catch handler, because the failure mode you care about is a rejected promise at startup rather than a thrown error at require time.
The trade-off of that pattern is that you lose coverage of the packaged app entirely. If a bug only reproduces in a built binary, Devtron will not be there to tell you what happened, and you would need a different instrumentation approach for that case.
getEvents() returns what the UI shows, with three caveats
The second API method exists so your main process can read the same data a human sees in the panel. It returns a Promise resolving to the array of IPC events recorded by the Devtron service worker since installation.
// main.js
import { devtron } from '@electron/devtron';
// Ensure Devtron is installed before calling getEvents()
devtron.install();
setInterval(async () => {
const ipcEvents = await devtron.getEvents();
console.log('IPC Events:', ipcEvents);
}, 2000);Three behaviours determine whether this is usable as instrumentation. Clicking Clear all events in the Devtron UI empties the array, so anything polling it sees the history disappear underneath. If the array exceeds 20,000 events it is truncated to the most recent 20,000. And if you call it before installation, or before the Devtron service worker is ready, you get an empty array rather than an error, which is the failure mode most likely to confuse you: an empty list means not ready, not nothing happened.
That last point drives the install-before-call ordering in the example, and it is the reason the README repeats the requirement rather than leaving it implicit.
Startup IPC is exactly what you will miss
This is the limitation to internalise before you trust the output. The README states that some IPC events sent immediately after the Electron app starts may not be captured, even if install() is called early, because Devtron may take a short time to initialize after the app starts.
That is the worst possible gap for the kind of bug people install a tool like this to find. Startup IPC is where capability negotiation, feature flags, window creation arguments and session setup happen, and it is also where a race between your app and the extension shows up. A missing early event can look identical to an app that never sent one.
The second caveat is about counting. ipcRenderer.once is tracked as two separate events, an on and a removeListener. So a listener registered with once inflates the event count by two, and any analysis you do on the event list has to account for the pairing or your picture of the messaging graph will be wrong.
The third is the session requirement, already noted: install() must precede session creation. Miss that and the events from that session are simply not there.
None of these make the tool useless. They make it a good tool for the steady state and an unreliable witness for the first moments of a run.
Three webpack configs and a service worker doing the recording
The package.json shows how the extension is assembled, and the three build targets map onto where the code has to run.
The build script runs clean, then build:node, then build:types, then build:browser. build:node uses webpack.node.config.ts and build:browser uses webpack.extension.config.ts, both run with cross-env NODE_OPTIONS='--import=tsx' so TypeScript configuration files load directly. build:types runs tsc -b against tsconfig.types.json to emit the declarations.
The exports map names those targets precisely. The root export offers types, an ESM build at ./dist/mjs/index.mjs and a CJS build at ./dist/cjs/index.cjs, so the same package serves both module systems. Two subpath exports exist for the pieces that must run in specific contexts: ./service-worker-preload and ./renderer-preload.
The service worker is where the recording actually happens, since getEvents resolves against events recorded by the Devtron service worker. That is why the API returns an empty array before the worker is ready rather than throwing, and it is why the project ships a preload for the service worker at all.
The repository root shows the rest: src/, spec/, spec-electron-setup/, three webpack configs, tsconfig.json, global.d.ts, manifest.json, postcss.config.js, tailwind.config.js, and a lint stack of oxfmt and oxlint configured through .oxfmtrc.json and .oxlintrc.json with husky and lint-staged wiring them into commits.
Development build, semantic release, and what it replaces
The state of the project is stated plainly at the top of the README: this project is under development and subject to change. Development is done with Yarn 4, pinned through.yarnrc.yml,.yarn/ and yarn.lock, with a.nvmrc for the Node version.
Releases are automated..releaserc.json is present and the version field in package.json is 0.0.0-development, which is the placeholder semantic-release reads before a release writes the real number. The published package is @electron/devtron, with v2.0.0 and v2.1.0 on 2025-09-04 and v2.1.1 on 2025-11-18. The last push was on 2026-10-10, so commits are landing after the most recent tag. Licence is MIT.
On what it replaces: the alternative is logging in both processes. Every send in the main process and every listener registration in the renderer gets a console.log, with a channel name and a timestamp, and you read the two streams side by side. That approach captures startup IPC, counts once-listeners correctly, and needs nothing installed, but it is noisy, it is easy to remove by accident, and it writes into logs you usually ship.
Devtron wins on ergonomics and on not polluting your output; the raw logging wins on completeness at startup and on being free of a dependency in production. If the bug you are chasing happens immediately after launch, the manual logging route may show more.
Editorial conclusion
Adopt Devtron if you maintain an Electron app whose main-to-renderer messaging has grown past what you can hold in your head, since a chronological IPC log in the DevTools panel is the cheapest possible substitute for logging every send and receive. Do not rely on it as a complete audit trail, because the README states that IPC sent immediately after startup may not be captured and that once-listeners are recorded as an on plus a removeListener pair. Before you add it, call devtron.install() in your main entry point before any session is created and gate it on app.isPackaged, since installation order is what determines what you can see.
Frequently asked questions
How do I install Devtron in an Electron app?
Import it in your main entry point and call install at the top of the file: `const { devtron } = require('@electron/devtron');` followed by `devtron.install();`. It then appears as a tab in your app's Developer Tools.
How do I keep Devtron out of production builds?
Guard the installation with app.isPackaged and use a dynamic import, as in the README example where installDevtron is called only when isDev is true and the failure is caught with console.error.
Will Devtron capture IPC sent at application startup?
Not reliably. The README states that some IPC events sent immediately after the app starts may not be captured even if install() is called early, because Devtron may take a short time to initialize.
How many IPC events does devtron.getEvents() keep?
At most 20,000. If the array exceeds that size it is truncated to the most recent 20,000 events, and it is also emptied when the Clear all events button in the Devtron UI is used.
What Electron version does Devtron require?
Electron 36.0.0 or higher. Devtron must also be installed before any newly created sessions, and ipcRenderer.once is recorded as two events, an on plus a removeListener.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/electron-devtron)Was this useful?