AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 32 — Desktop Apps from Web Skills — Three Ways to Draw the Same Window

32 — Desktop Apps from Web Skills — Three Ways to Draw the Same Window

August 12, 20269 min read
Download as Markdown

The thing I had to straighten out for desktop apps wasn't a new language — it was a choice I didn't realize was a choice. Every cross-platform desktop framework answers the same two questions, and the answers are the whole tradeoff: _how does the UI get rendered on screen,_ and _what runs as the backend (the part with file access, native APIs, the real machine)?_ Once I could see those two axes, the sea of names — Electron, Tauri, Flutter, NW.js — collapsed into a small grid of decisions, each with a price tag attached.

The payoff is that the same web skills (HTML, CSS, JavaScript) can ship a windowed, installable app that reads and writes files, shows a dock icon, and feels native — without me learning Swift, WinUI, or Qt. The cost lives entirely in those two answers.

Electron bundled browser + Node UI: bundled Chromium renders HTML/CSS/JS Backend: Node.js fs, native APIs max compatibility heavy bundle (~100MB+) VS Code, Discord, Slack Tauri system webview + Rust UI: OS native webview WebView2 / WKWebView Backend: Rust compiled, sandboxed commands tiny & secure bundle ~3–10MB 1Password, new-wave tools Flutter custom renderer + Dart UI: own rendering engine Skia / Impeller draws pixels Backend: Dart single language, AOT compiled pixel-consistent UI desktop + mobile + web Google ads, Alibaba apps

Electron — ship the browser with the app

Electron's answer to both questions is the biggest possible: it bundles a full Chromium browser and a full Node.js runtime inside every app [1]. The UI is a web page rendered by that bundled Chromium; the backend is Node, with full access to the file system, native menus, tray icons, and OS dialogs. The two halves talk to each other over an inter-process bridge (a message channel between them) — the "main" process (Node) and the "renderers" (Chromium windows).

What clicked for me is why the bundle is so large: I'm shipping a browser _inside_ my installer. That's also the superpower. The app behaves identically on Windows, macOS, and Linux because it ignores each OS's native UI toolkit and renders everything in its own Chromium. There is no "depends on the user's browser version" — the version _is_ the one I shipped [2].

The tradeoffs that follow from that decision:

  • Bundle size and memory. Every Electron app carries a copy of Chromium plus Node. Installers land in the tens to hundreds of megabytes, and a "hello world" window sits on hundreds of MB of RAM because there's a whole browser engine underneath. VS Code, Discord, and Slack are Electron — and the "Electron uses a lot of RAM" reputation comes straight from this design [1].
  • Max compatibility, lowest surprise. The upside of shipping the browser is that any web tech that works in Chromium works here, full stop. No native-webview quirks per OS.
  • Rapid development. The same React/Vue/vanilla JS I use for the web becomes a desktop app with a tiny bridge for native calls. For a web developer, the learning curve is mostly "where does the file-access code live," not "how do I draw a window."

Electron is the safe default when I value development speed and UI consistency over footprint, and it's the most battle-tested of the three — the apps I use every day are built on it.

Tauri — borrow the browser the OS already has

Tauri flips both answers [3]. The UI is still a web page — same HTML/CSS/JS, same React if I want — but it is rendered by the webview the operating system already ships — a webview is the OS's built-in browser component for embedding a web page: WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux. No bundled Chromium. The backend, meanwhile, is a small Rust binary that compiles to a native executable, exposing only the commands I explicitly allow.

The mental shift: _the browser is not part of my app anymore._ It's a reusable OS component my app asks to render a window. That single decision reshapes every tradeoff:

  • Tiny bundles. Without Chromium in the installer, a Tauri app lands in the low single-digit megabytes — often 10–20× smaller than the equivalent Electron app [4].
  • Performance and memory. A compiled Rust backend and no second browser engine mean a much lighter footprint on disk and in RAM.
  • Security by allowlist. Native access isn't blanket-granted the way Node grants it in Electron. The Rust core only exposes the JS frontend to commands I declare, and capabilities are scoped per-window [3]. The default posture is "the web layer can't touch the OS unless I said so."
  • The cost: webview inconsistency. Because Tauri uses whatever webview the OS provides, I'm back in "depends on the user's browser engine" territory. Different platforms ship different webview versions with slightly different quirks. For most modern CSS/JS it's a non-issue, but it's the price of not bundling.

Tauri is the pick when footprint, security, and performance matter — small utilities, internal tools distributed widely, anything where "this is a 150MB chat app" would be embarrassing.

Flutter — skip the browser entirely

Flutter takes the most radical route: it doesn't use a browser or an OS webview at all [5]. The UI is described in Dart (not JS), and Flutter's own rendering engine (Skia, and now Impeller) paints every pixel directly to a window. There's no HTML, no DOM, no CSS — the "widgets" are Dart objects that the engine turns into drawn shapes.

This is the option that doesn't really reuse my web skills — Dart isn't JavaScript, and the widget tree isn't HTML. What I trade in familiarity, I buy back in consistency:

  • One codebase, truly. Flutter compiles the same Dart to Windows, macOS, Linux, iOS, Android, and the web [5][6]. The rendering engine is the same everywhere, so a button looks identical across platforms — because nothing is delegated to a native toolkit.
  • Pixel consistency and smooth animation. Because Flutter owns the paint pipeline, it sidesteps the per-platform rendering quirks that haunt both webview-based options. Animations hit a consistent frame budget (frames per second) on every target.
  • The cost: a new language and way of thinking. Flutter is desktop-capable, but its center of gravity is mobile. The desktop story is real and improving, but the ecosystem, patterns, and most tutorials are mobile-first. Coming from web, I'm learning Dart and a widget tree, not reusing HTML/CSS muscle memory [6].

Flutter earns its place when the goal is a product that spans mobile and desktop from one codebase with identical visuals, and I'm willing to invest in Dart up front.

The decision, compressed

The three options aren't points on a line; they're three corners of a triangle, each optimized for a different thing:

  • Electron — bundle the browser, get perfect web compatibility and fast shipping, accept the weight. Powers the heavyweights.
  • Tauri — borrow the OS webview, write the core in Rust, get tiny and secure apps, accept per-platform webview variance.
  • Flutter — render everything yourself in Dart, get pixel-identical cross-platform UI including mobile, accept a new language and a non-web way of thinking.

How I use this

The practical habit these notes left me with is a single triage question before reaching for a framework: _what's the backend, and what's drawing the pixels?_ If the answer is "Node everywhere and I need it out the door," it's Electron. If the answer is "as small and locked-down as possible," it's Tauri. If the answer is "mobile and desktop, same UI, I'll learn Dart," it's Flutter. Most of the "which is best" noise dissolves once I notice the three aren't competing on the same axis — they each picked a different thing to optimize.

References

[1] OpenJS Foundation, "Electron — Build cross-platform desktop apps with JavaScript, HTML, and CSS," Electron Documentation, 2024. [Online]. Available: https://www.electronjs.org/docs/latest/

[2] OpenJS Foundation, "Process Model," Electron Documentation, 2024. [Online]. Available: https://www.electronjs.org/docs/latest/tutorial/process-model

[3] Tauri Programme within the Codex Project, "Tauri — Build smaller, faster, and more secure desktop applications with a web frontend," Tauri Documentation, 2024. [Online]. Available: https://tauri.app/

[4] Tauri Programme, "Tauri vs. Electron," Tauri Documentation, 2024. [Online]. Available: https://v2.tauri.app/concepts/

[5] Google, "Flutter — Build apps for any screen," Flutter Documentation, 2024. [Online]. Available: https://flutter.dev/

[6] Google, "Flutter for desktop," Flutter Documentation, 2024. [Online]. Available: https://flutter.dev/multi-platform/desktop

Knowledge check · Question 1 of 5

What does Electron bundle inside each app to render its UI?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!