When I needed a cross-platform desktop SSH client, the tauri vs electron question settled fast: I picked Tauri. The deciding factors were binary size, idle memory, and a Rust backend I could actually trust with the privileged parts. Electron bundles a full Chromium runtime, which means roughly 100MB of download and a heavy resident memory footprint before your app does anything. Tauri ships a few-megabyte binary because it renders through the operating system's existing webview instead of carrying its own browser. That single architectural choice is the whole story, and it comes with a real cost I will be honest about below.
What actually makes the binary so much smaller?
Electron embeds Chromium and Node.js in every build. You get the exact same V8, the exact same Blink renderer, and the exact same Node runtime on Windows, macOS, and Linux. That uniformity is genuinely useful, but you pay for it in bytes: a trivial Electron app starts near 100MB and climbs from there. Tauri does the opposite. It links a small Rust core and asks the host OS to supply the webview: WebView2 (Chromium-based, Microsoft-shipped) on Windows, WKWebView on macOS, and WebKitGTK on Linux. Nothing browser-sized is bundled, so a release build of my SSH client lands in the single-digit megabytes and a cold start does not have to boot Chromium first.
Idle memory follows the same pattern. There is no second copy of a browser engine sitting in RAM; the webview process is the one the OS already knows how to manage. For a tool I leave open all day next to a dozen other windows, that difference is felt, not theoretical.
Why did the Rust backend matter for a security tool?
An SSH client handles private keys, host-key trust decisions, and a live encrypted socket. I did not want that logic living in a JavaScript main process. In Tauri the privileged code is Rust, exposed to the webview through a narrow, typed command bridge. The frontend is pure UI; it cannot touch the filesystem or open a socket on its own. It calls a #[tauri::command] function, and Rust decides what is allowed. That seam is small enough to audit, and Tauri v2's capability and permission model lets me deny the webview everything except the exact commands I registered. I dug into the russh and tokio side of this in my offline SSH client built with Tauri, Rust, and xterm.js, where the crypto and socket I/O never leave the Rust layer.
// The webview can only call commands you register here.
// Everything privileged stays on the Rust side of the bridge.
#[tauri::command]
async fn connect(host: String, port: u16) -> Result<String, String> {
// Rust owns the socket, the keys, and the host-key check.
open_session(&host, port).await.map_err(|e| e.to_string())
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![connect])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}The attack surface argument is the practical version of this: there is no bundled Node with its module ecosystem in the trusted process, and the webview is sandboxed away from system APIs unless you explicitly grant a capability. You still have to write that allowlist correctly, but the default posture leans closed instead of open.
What does Electron still do better?
This is where I push back on the Tauri hype. Electron is the right call in several real situations, and pretending otherwise wastes people's time:
- One identical engine everywhere. Because Chromium is bundled, every user runs the same renderer at the same version. You never debug a CSS or JS quirk that only appears on one platform's webview.
- A far larger ecosystem. Years of Electron plugins, native modules, auto-updaters, and Stack Overflow answers exist. Tauri's ecosystem is healthy and growing but smaller, so you write more yourself.
- Node in the main process. If your team lives in JavaScript and your backend logic is already Node, Electron lets you reuse it directly instead of porting it to Rust.
- Mature tooling for huge apps. VS Code, Slack, and Discord are Electron for a reason: at that scale the runtime cost is acceptable and the predictability pays off.
Tauri trades a bundled browser for the OS webview. You get a tiny binary and a Rust core; the bill arrives as webview differences you now own.
So what is the real tradeoff?
The cost of the small binary is that you inherit the host's webview, and those webviews are not identical. WebView2 and WKWebView are close to modern Chromium and Safari, but WebKitGTK on Linux can lag, especially on older distributions, and that is exactly where rendering and JS-API differences bite. You test on three engines, not one. The second cost is Rust: anything privileged is Rust, so a team allergic to it will feel friction. For me both costs were acceptable. I am comfortable in Rust, and a terminal UI does not lean on bleeding-edge CSS, so the webview spread stayed manageable. If your app is a heavy WebGL canvas or depends on the newest browser APIs, weigh that Linux variance carefully before committing.
Packaging is the other place the abstraction shows. Tauri produces native installers per platform (.msi via WiX and .exe via NSIS on Windows, .dmg on macOS, .deb/.rpm/.AppImage on Linux), and getting signing and per-OS quirks right took real effort, which I wrote up separately in cross-platform packaging for a Tauri app. The official v2 bundler documentation is worth reading before your first release build rather than after a broken one.
If I were shipping a JavaScript-heavy product with a large team and a deadline, I would reach for Electron without apology. But for a focused security tool where the binary should be small, the memory low, and the privileged code auditable in a language built for it, Tauri was the correct trade. The few-megabyte download and the Rust seam around every dangerous operation were worth the webview testing tax. Decide on your own axes: if binary size, memory, and a trustworthy native core matter more than a single uniform engine and a giant plugin catalog, Tauri earns the spot.

