<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: yyzTools</title>
    <description>The latest articles on DEV Community by yyzTools (@jearry).</description>
    <link>https://dev.to/jearry</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4087854%2F1f359415-861e-402d-aec7-053c30749c2e.png</url>
      <title>DEV Community: yyzTools</title>
      <link>https://dev.to/jearry</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jearry"/>
    <language>en</language>
    <item>
      <title>Why I Built a Windows Desktop App with C++ + WebView2 Instead of Electron</title>
      <dc:creator>yyzTools</dc:creator>
      <pubDate>Fri, 21 Aug 2026 08:40:17 +0000</pubDate>
      <link>https://dev.to/jearry/why-i-built-a-windows-desktop-app-with-c-webview2-instead-of-electron-2o69</link>
      <guid>https://dev.to/jearry/why-i-built-a-windows-desktop-app-with-c-webview2-instead-of-electron-2o69</guid>
      <description>&lt;p&gt;&lt;em&gt;I'm building &lt;a href="https://yyztools.com" rel="noopener noreferrer"&gt;yyzTools&lt;/a&gt;, a Windows productivity suite with 40+ tools fused into one install. This is the architecture decision I get asked about most.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The default path
&lt;/h2&gt;

&lt;p&gt;When a developer today wants to build a desktop app, the default reflex is Electron. It's rational: HTML/CSS/JS, cross-platform, mature ecosystem (VS Code, Slack, Discord proved it works). If your team is frontend-heavy, it's almost a non-decision.&lt;/p&gt;

&lt;p&gt;I didn't take that path. yyzTools is C++ (Win32) + WebView2 + Alpine.js + Vite. Windows-only. No bundled Chromium.&lt;/p&gt;

&lt;p&gt;This post isn't "Electron is bad." It's "for a resident, lightweight, multi-feature desktop tool, Electron's costs outweigh its benefits, and here's what I picked instead."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Electron didn't fit this project
&lt;/h2&gt;

&lt;p&gt;The key word is &lt;strong&gt;resident&lt;/strong&gt;. yyzTools is a tool you install once and it sits in your tray, running every time you hit &lt;code&gt;Alt+Space&lt;/code&gt; for the command palette, grab from clipboard history, fire OCR. It's not an app you open and close—it's always there.&lt;/p&gt;

&lt;p&gt;Electron's model is "bundle a full Chromium per app." For an app you open occasionally, the ~200MB memory and multi-second startup is tolerable. For something that's &lt;strong&gt;always resident&lt;/strong&gt;, it's a tax you pay every second the machine is on.&lt;/p&gt;

&lt;p&gt;Nobody wants a clipboard manager + launcher eating 400MB of RAM just by being installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The alternative: WebView2 + a C++ host
&lt;/h2&gt;

&lt;p&gt;Microsoft's WebView2 is a control that renders web content using the &lt;strong&gt;Edge engine already installed on Windows&lt;/strong&gt;. No bundled browser. The runtime ships with Windows 11; on Windows 10 the installer guides the user.&lt;/p&gt;

&lt;p&gt;So my stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C++ / Win32 host&lt;/strong&gt; — owns the window, system calls, process management, all native work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebView2&lt;/strong&gt; — renders the frontend pages using the system Edge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alpine.js (~3KB)&lt;/strong&gt; — frontend framework, no virtual DOM, no heavy build chain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite multi-entry&lt;/strong&gt; — each feature page (command palette, OCR, translate, download...) is an independent bundle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture is a four-layer sandwich:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend pages (Alpine.js + vanilla JS)
        ↓  via web/lib/zen_api.js (unified wrapper)
WebView2 bridge layer
   BindSync  → synchronous return (most methods)
   BindAsync → async callback (OCR, app-info)
        ↓
C++ NativeApi layer (FileManager / ProcessManager / ...)
        ↓
Win32 system API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend never calls &lt;code&gt;window.Zen&lt;/code&gt; directly. It goes through a &lt;code&gt;ZenAPI&lt;/code&gt; wrapper class that normalizes return values—the C++ side has two JSON paths (hand-rolled strings vs &lt;code&gt;boost::property_tree&lt;/code&gt;) where the same &lt;code&gt;error&lt;/code&gt; field can be number &lt;code&gt;0&lt;/code&gt; or string &lt;code&gt;"0"&lt;/code&gt;. The wrapper does &lt;code&gt;String(res.error) === '0'&lt;/code&gt; to paper over it. (That's documented debt, not a hidden clever trick.)&lt;/p&gt;

&lt;h2&gt;
  
  
  What this buys
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lower memory.&lt;/strong&gt; No Chromium process resident. The main process is a thin C++ host plus the system Edge it borrows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster startup.&lt;/strong&gt; No spinning up a fresh Chromium instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller disk.&lt;/strong&gt; The web runtime is shared across the system, not bundled per-app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native when needed.&lt;/strong&gt; File ops, process management, OCR engine calls, screen recording—all C++, no IPC to a JS-land bridge for hot paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;Let's be honest about the tradeoffs, because this isn't a free lunch:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows-only.&lt;/strong&gt; WebView2 is Microsoft's. If you want Mac/Linux, you need Electron or a full Qt port. yyzTools targets Windows, so this is fine—but it's the biggest constraint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A hand-written bridge.&lt;/strong&gt; Electron gives you mature IPC for free. With WebView2 you hand-roll the JS↔C++ channel (a "method name → callable" registry, sync/async variants, thread marshaling for async callbacks back to the UI thread). It's not hard, but it's code you own and maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No type system on the boundary.&lt;/strong&gt; The &lt;code&gt;error&lt;/code&gt; field type drift I mentioned isn't caught by TypeScript—it's a runtime contract between C++ and JS. I normalize at the wrapper, but every new native method is a chance to introduce a new type mismatch. A typed IPC (like tRPC over a real protocol) would be safer; I chose simplicity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alpine.js means no React ecosystem.&lt;/strong&gt; Alpine is great for "many simple pages" (which a productivity suite is). But if I wanted a complex single-page app with a rich component library, I'd miss React's ecosystem. The pages here are intentionally simple—search box, settings panel, result grid—so Alpine fits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need C++ people.&lt;/strong&gt; A pure-frontend team can't extend the native side. This is a real org constraint. If your team can't staff Win32 expertise, Electron is the pragmatic call, period.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you'd make the same call
&lt;/h2&gt;

&lt;p&gt;I'd pick this again specifically when all four are true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The app is resident&lt;/strong&gt; (tray, autostart, always-on) — the memory tax compounds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows is the target&lt;/strong&gt; — WebView2 is Windows-only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need native system access&lt;/strong&gt; — file/process/clipboard/OCR, where C++ beats a JS bridge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You have C++ capacity&lt;/strong&gt; — someone can own the host and bridge&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any of those is false, Electron (or Tauri) probably wins. yyzTools happened to clear all four, so the C++/WebView2 path was correct here. It would be wrong for a cross-platform app, or a resident app with no native API needs, or a team of pure-frontend devs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;"Electron vs native" isn't a moral question—it's a fit question. The reflexive "always Electron" is wrong for resident, native-heavy, Windows-targeted tools. The reflexive "never Electron" is wrong for cross-platform or frontend-heavy teams.&lt;/p&gt;

&lt;p&gt;The useful question to ask when you start a desktop project: &lt;strong&gt;is this app resident, and does it need native system access?&lt;/strong&gt; If yes to both and you're on Windows, the C++ + WebView2 path is worth the C++ staffing cost. If no, Electron's defaults will serve you faster.&lt;/p&gt;

&lt;p&gt;yyzTools is the former. Your project might be the latter. Pick on fit, not on what's trendy.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm building yyzTools — a free, local-first Windows productivity suite. Website: &lt;a href="https://yyztools.com" rel="noopener noreferrer"&gt;yyztools.com&lt;/a&gt;. I write about the architecture decisions behind it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>cpp</category>
      <category>productivity</category>
      <category>software</category>
    </item>
  </channel>
</rss>
