Electron
Engineering guide for Electron — process model, security, performance, packaging, auto-update, and native integration
Electron
Engineering knowledge for building production-grade desktop apps with Electron. Skips Chromium/Node basics in favor of what actually determines whether a desktop app ships safely and runs well: the process model, the security boundary between main and renderer, startup and memory cost, code signing/notarization, and reliable auto-update.
Topics
Best Practices
Project structure, preload contracts, typed IPC, window lifecycle, single instance
Pitfalls
nodeIntegration, remote module, blocking the main process, path/asar gotchas
Process Model
Main / renderer / preload, contextBridge, IPC patterns, MessagePort, utility process
Security
contextIsolation, sandbox, CSP, navigation control, permission handlers, secure defaults
Performance
Startup time, V8 snapshots, memory per window, bundle size, BrowserWindow reuse
Native Integration
Native modules, N-API, tray/menu, global shortcuts, protocols, deep links, OS APIs
Packaging
electron-builder vs Forge, code signing, macOS notarization, installers, asar
Auto-Update
electron-updater, Squirrel, update servers, staged rollout, delta updates
Debugging
DevTools, main-process inspector, crash reporting, logging, production diagnostics
Testing
Unit, Playwright for Electron, WebdriverIO, IPC mocking, CI on headless runners
CI/CD
Multi-OS build matrix, signing in CI, release publishing, artifact caching
Process Architecture
Electron App
├── Main Process Single Node.js process — app lifecycle, owns native
│ ├── App lifecycle ready / window-all-closed / activate / before-quit
│ ├── Window management BrowserWindow creation, state, multi-window
│ ├── Native surface Menu / Tray / dialog / Notification / global shortcut
│ └── Privileged I/O fs / child_process / DB / OS APIs (never in renderer)
├── Preload Script Isolated bridge — runs before page, has limited Node
│ └── contextBridge Exposes a narrow, typed API to the renderer
├── Renderer Process One Chromium process per window — your UI
│ ├── UI framework React / Vue / Svelte — same as web
│ └── ipcRenderer Talks to main only through the preload contract
└── Utility Process Optional sandboxed Node child for CPU/IO work
└── MessagePort Direct channel, keeps the main process responsiveRecommended Learning Path
| Stage | Focus | Key Outcome |
|---|---|---|
| Post-basics | Main vs renderer, BrowserWindow, loading a page | A window that renders your app |
| Bridge | Preload, contextBridge, typed IPC | Renderer and main talk without leaking Node |
| Security | contextIsolation, sandbox, CSP, navigation guards | No remote code can touch the OS |
| Native | Menus, tray, shortcuts, native modules, protocols | A real desktop app, not a hosted web page |
| Performance | Startup, memory per window, V8 snapshots | Fast launch, low idle footprint |
| Distribution | Code signing, notarization, installers | Users can install without OS warnings |
| Production | Auto-update, crash reporting, testing, CI/CD | Ship and keep shipping safely |
Why focus on practice over syntax
Electron's docs cover the APIs well, but they cannot make the architectural decisions for you. What determines whether an Electron app is safe and pleasant to use is the security posture of the renderer, keeping the main process unblocked, a disciplined IPC contract, and a signed, auto-updating release pipeline. Get these wrong and the app either leaks the OS to untrusted content or frustrates users with jank and broken updates — these pages consolidate the lessons that usually come only from shipping one.
Electron is not the only option. Tauri (Rust + system WebView) produces far smaller binaries and a smaller attack surface; native toolkits win on footprint. Electron's advantage is a single bundled Chromium — pixel-identical rendering across OSes and full control of the runtime version. Choose it when consistency and web-stack reuse matter more than binary size.