Electron
Electron packages a web app with Chromium and Node.js into a desktop application — one codebase shipping to Windows, macOS, and Linux with full OS access. VS Code, Slack, Discord, Figma's desktop app, 1Password, Notion, WhatsApp Desktop: a striking share of the software on your machine is a web app in an Electron shell.
The trade is famous in both directions: your web team ships desktop software with native menus, file access, and auto-update — and every app carries its own ~100+ MB browser and Node runtime, with the memory bill to match. That trade-off (and the rise of Tauri, which swaps bundled Chromium for the OS's webview and a Rust core) defines the modern decision space for desktop-from-web.
TL;DR
- Electron = Chromium (UI) + Node.js (system access) + native glue, in one distributable app.
- Two process types: the main process (Node — windows, menus, files, OS APIs) and renderer processes (Chromium — your web UI). They talk via IPC.
- Security is the discipline: renderers showing any remote content must run sandboxed with
contextIsolation, exposing only a minimal, validated API through a preload script — never raw Node in the renderer. - Packaging/updates are a solved-but-real project: Electron Forge/Builder for installers, code signing + notarization (mandatory on macOS), and auto-update infrastructure.
- Performance is a practice, not a verdict — VS Code proves Electron can feel native; unoptimized ports prove it can feel like a browser tab eating 800 MB.
- Tauri is the main alternative: OS webview + Rust backend → ~10 MB binaries and low memory, at the cost of cross-platform rendering variance and a Rust boundary.
Quick Example
The canonical minimal app — main process, secure preload bridge, renderer:
The renderer never sees fs or require — it sees window.api.openFile(). That shape is Electron security.
Core Concepts
Main vs Renderer
The division of labor rule: renderers render; main does everything privileged. Heavy CPU work belongs in neither — use a worker thread or utilityProcess, or the UI (main and renderers block on a busy main process) stutters.
The Security Model
An Electron app is a browser where XSS can become remote code execution on the user's machine — if you've wired it badly. The modern defaults exist for that reason:
contextIsolation: true+sandbox: trueon every window — renderer JS and preload run in separate worlds; the page can't reach Node.- Preload scripts expose capabilities, not power:
openFile(), notipcRendereritself, and never a generic "run anything" channel. Validate every IPC input in main — treat renderers as untrusted, especially any window that ever displays remote or user-generated content. - No
nodeIntegration: true. Ever. Tutorials that suggest it are teaching pre-2020 Electron. - Standard web armor still applies: CSP headers, no loading remote code over HTTP,
shell.openExternalallowlists. The XSS you shrugged at on the web is a workstation compromise here — see Electron's own checklist.
Ship It: Packaging, Signing, Updates
The unglamorous half that dominates real Electron work:
Performance as a Practice
The gap between "Electron is bloated" and VS Code is engineering:
- Defer and split: lazy-load features, keep startup path minimal (measure it — users judge desktop apps on launch).
- Windows are expensive — each renderer is a Chromium process (~50–100 MB baseline). Reuse and pool them.
- Move work off the UI paths: worker threads/utility processes for indexing, parsing, crypto; native modules (or WASM) for hot loops.
- Profile like a web app (it is one): Chromium DevTools work fully — see Web Performance habits.
Electron vs Tauri (and the Field)
The decision usually reduces to: rendering consistency and JS-everywhere (Electron) vs footprint and Rust-grade native layer (Tauri). Cross-platform webview differences are Tauri's real tax — you're back to testing three browser engines, which Electron's bundling specifically abolished. Adjacent options: Wails (Go + webview, Tauri-shaped), Flutter desktop and .NET MAUI (non-web UI stacks), and PWAs (installable web apps) when you don't actually need deep OS integration — always worth asking first.
Common Mistakes
nodeIntegration: true to "Make It Work"
The classic shortcut that turns any renderer XSS into arbitrary code execution. The preload/contextBridge pattern exists precisely so you never do this — if a library demands it, the library is the problem.
Exposing Generic IPC Power
contextBridge.exposeInMainWorld("ipc", ipcRenderer) (or an invoke(anyChannel, anyArgs) passthrough) recreates the hole with extra steps. Expose named, purpose-specific, input-validated functions.
Blocking the Main Process
Synchronous fs calls, big JSON parses, or heavy computation in main freeze every window's input handling. Main is an event-loop traffic controller — keep it async and thin.
Treating Packaging as a Last-Week Task
Signing certificates, notarization queues, auto-update servers, and per-OS installer quirks reliably consume weeks. Stand up the Forge/CI/signing pipeline in week one with a hello-world build.
Shipping on an Ancient Electron
Old Electron = old Chromium = shipping known-exploitable browser CVEs inside your app. Track supported majors; automate the upgrade PRs.
FAQ
Why do so many companies choose Electron despite the memory cost?
Because the alternative is usually three native teams or no desktop app. One web codebase covering Windows/macOS/Linux (often sharing most code with the web product), hiring from the largest talent pool, and a proven ceiling (VS Code, Figma, Slack) outweigh RAM for most products. Users complain about memory; they buy features.
Electron or Tauri for a new app?
Tauri deserves the default look for new, footprint-sensitive apps — the size/memory win is dramatic, and 2.0 matured it considerably. Electron remains the safer pick when you need pixel-identical rendering across platforms, depend on Chromium-only APIs, want zero Rust, or lean on its deeper ecosystem (auto-update infra, native modules, a decade of Stack Overflow).
Can I reuse my React/Vue web app?
Largely, yes — the renderer is a browser, so your SPA runs nearly unchanged; the work is the desktop layer (menus, IPC-backed features, offline files, updates). Keep web and desktop sharing a core via a monorepo, with platform adapters at the edge.
How do Electron apps talk to the OS?
Through the main process: Electron's own APIs (dialogs, tray, notifications, clipboard, global shortcuts) cover most needs; Node native addons or spawned helpers cover the rest. The renderer always asks via IPC — that boundary is the architecture.
Is Electron dying?
The "bloat" discourse is old; the usage isn't declining — Chromium-based Electron still ships the most-used developer and collaboration tools on desktop, and the project tracks Chromium aggressively. What's changed is real competition (Tauri) finally pressuring the footprint — good news whichever you pick.
Related Topics
- Node.js — The main process's runtime
- React — The most common renderer stack
- PWA — The lighter-weight question to ask first
- XSS — Why renderer security is existential here
- Web Performance — The same profiling, higher stakes
- Monorepos — Sharing code between web and desktop
- GitHub Actions — The three-OS build matrix