This article is published in English.
Choosing a JavaScript GUI Stack by What Your Users Actually Download
Learn why desktop shells and component libraries are separate layers, and how to pick Electron, Tauri, MUI, shadcn/ui and others by shipped weight.
Searching for a "JavaScript GUI toolkit" tends to produce lists that put Electron next to Material UI as if they were competitors. They are not: one gives you an installable window, the other gives you buttons to put in it, and most real products need one of each. This guide separates the two layers, walks through the main options in each, and applies a single deciding question to both: how much code ends up on your users' machines, and how much of it did you actually need?
Two layers that other languages bundle together
In ecosystems such as Qt, GTK or WinForms, a GUI toolkit does everything at once: it opens a native window and supplies the widgets that live inside it. JavaScript divides that responsibility between two kinds of tools, and mixing them up is what makes most comparisons confusing.
- Desktop shells package web code as an installable application. What they supply is the application frame: a native window, tray integration, access to local files and an installer. Widgets are not part of the deal.
- Component libraries supply the interactive pieces, such as buttons, tables, date pickers and dialogs. They execute in a browser and are indifferent to whether that browser is embedded in a desktop app or is an ordinary tab.
A desktop product might combine Tauri with MUI; a web product might use MUI alone. Choosing a shell and choosing a component library are two separate decisions.
The question that actually separates the options
In some ecosystems licensing is the decisive factor, because the wrong binding can force you to open-source your product. In the JavaScript GUI world nearly everything is MIT-licensed, so the license rarely narrows the field. What does narrow it is weight. For shells, the gap is between an installer of a few megabytes and one of a couple of hundred. For component libraries, the gap is between shipping a whole design system and shipping only the handful of components you really use. Keep that question in view through both halves below.
The version numbers quoted here reflect npm at the time of writing; check the registry before relying on them. For a deeper look at the desktop side specifically, including newer runtimes, see our comparison of Electron, Tauri, Electrobun and Deno desktop options.
Desktop shells
Electron: the proven default that ships its own browser
Electron (v44.3.0 at the time of writing, MIT) is added as a development dependency:
npm install --save-dev electron
It packages a complete Chromium browser plus a Node.js runtime with your application. The UI is a web page and the backend is Node. Its strongest argument is maturity: VS Code, Slack, Discord, Figma and 1Password are all built on it, and packaging, auto-updates, code signing and crash reporting have well-documented tooling that many teams already know.
The second advantage is consistent rendering. Because you ship the browser, the app looks identical on Windows, macOS and Linux, and you test against one Chromium version rather than three different system webviews. It is also JavaScript end to end, so any frontend developer can work on the main process.
The cost is that every app carries a browser. Bundles commonly land between 80 and 200 MB, and because every Electron app starts a separate Chromium, RAM consumption adds up fast for a user running several of them. Patching that embedded Chromium is also your responsibility, on your own release schedule.
Tauri: the system webview plus a Rust backend
Tauri (v2.11.4 at the time of writing, dual-licensed Apache-2.0 or MIT) is scaffolded with its own create command:
npm create tauri-app@latest
Rather than bundling a browser, Tauri uses the webview that the operating system already provides, and its backend is written in Rust instead of Node. The size difference is structural rather than incremental: commonly reported figures put Tauri bundles at roughly 3 to 10 MB versus 120 to 200 MB for Electron, with memory use 50 to 75 percent lower and faster startup. Because the gap comes from architecture, not tuning, it has held across releases.
The security model differs too. In Electron, frontend JavaScript can reach the operating system through Node unless you lock it down. In Tauri, the frontend starts with no system access; privileged operations are Rust functions the frontend invokes by name, and Tauri v2 introduced a capability system that controls which APIs each window may call. Tauri 2 also targets iOS and Android, something Electron does not do at all, which matters to teams that want one codebase across desktop and mobile.
The downsides are real. Each platform's own webview renders slightly differently, so you are now testing three engines instead of one. Anything beyond the frontend requires some Rust. On Windows, Tauri relies on WebView2, which is present on virtually all modern installations but occasionally needs a bootstrapper. A sensible rule: if bundle size is not yet a customer complaint, picking Tauri purely to shave megabytes is optimizing too early. Reach for it when compact installers, a light memory footprint, stricter isolation or a mobile build are actual requirements.
NW.js: the older Chromium bundler
NW.js (v0.115.0 at the time of writing, MIT) installs as a regular package:
npm install nw
It also bundles Chromium and in fact predates Electron. Its distinguishing trait is that Node and the DOM share one context, so a web page can call Node APIs directly, without Electron's split between main and renderer processes. For some applications that is simpler to reason about. The trade-off is a far smaller community: less learning material, less packaging tooling and less help when you get stuck, while bundles stay as large as Electron's.
Neutralino: the smallest possible wrapper
Neutralino (CLI v11.7.2 at the time of writing, MIT) is driven by a globally installed CLI:
npm install -g @neutralinojs/neu
It is a lightweight native binary around the system webview, with neither Node nor Chromium. Bundles are typically 1 to 5 MB, smaller even than Tauri because there is less framework involved. If all you need is to wrap an existing web interface and add a tray icon, simple persistence and limited file access, Neutralino is the least machinery that gets the job done. It has the smallest ecosystem and the narrowest set of native capabilities of the four, so treat it as a utility framework rather than a platform for complex applications.
Component libraries
Everything in this half runs in a browser, so each option works equally well inside a desktop shell or a normal tab. Here the weight question shifts from installer size to how much library code your bundle drags along.
MUI: maximum coverage, Material by default
MUI (v9.4.0 at the time of writing, MIT) is installed together with its Emotion styling engine:
npm install @mui/material @emotion/react @emotion/styled
Among React component libraries it is the biggest and longest-established, and it follows Google's Material Design system. Component coverage is enormous, the documentation is excellent, and its data grid handles serious data volumes. If you need a component, MUI almost certainly has it, and someone has already asked your question about it.
It is also a lot of library. A fresh install measured around 19 MB in node_modules; that is not what reaches users, but it signals scope. Your shipped bundle depends on tree-shaking working properly, and unless you invest in theming, everything looks like Material Design, which some teams want and others find confining.
shadcn/ui: copy the source instead of adding a dependency
shadcn/ui (CLI v4.21.0 at the time of writing, MIT) is not a package you import. Its CLI initializes a project and then copies individual components into it:
npx shadcn@latest init
npx shadcn@latest add button dialog
Each component combines Radix UI primitives with Tailwind styling, and after the CLI runs it is just source code living in your repository. If a button needs different behavior, you edit the button; there is no wrapper component, no theming API to fight and no maintainer to persuade. It also keeps the bundle honest: add four components and only those four end up in your build.
The price of ownership is maintenance. No npm update will improve your components; upgrading means re-copying and reconciling changes by hand. It also assumes Tailwind, which makes it a poor match for projects that do not use it. The model works because it flips the usual trade-off between convenience and customization: you get a solid starting point and full control, and in exchange updates become your problem.
Ant Design: built for dense enterprise screens
Ant Design (v6.6.3 at the time of writing, MIT) comes from Alibaba and installs as a single package:
npm install antd
It offers the most complete set of components for data-heavy business interfaces: advanced tables, complex forms, transfer lists, tree selects. For admin consoles and internal tools, the widget you need probably already exists. It is also the heaviest library in this list, around 61 MB on disk after install, roughly three times MUI, and its strong visual identity is noticeably harder to move away from than Material's.
Mantine: good defaults without heavy opinions
Mantine (v9.6.1 at the time of writing, MIT) splits its core components from a hooks package:
npm install @mantine/core @mantine/hooks
Teams that find MUI too opinionated and shadcn/ui too hands-on often land here. It has a large component set, sensible defaults, straightforward theming, strong TypeScript support and solid dark mode support without extra setup. Even taken separately, the hooks package, with helpers for click-outside detection, local storage and media queries, is useful even on its own. Its community is smaller than MUI's or Ant Design's, so expect fewer third-party extensions and fewer ready-made answers.
Radix UI and Headless UI: behavior without styling
Radix UI (v1.1.23 at the time of writing) and Headless UI (v2.2.10), both MIT, are installed per primitive or as a single package respectively:
npm install @radix-ui/react-dialog
npm install @headlessui/react
These are unstyled primitives. They take care of behavior, keyboard navigation, focus management and accessibility, and leave every visual decision to you. That division is valuable because accessible widgets are harder than they look. A proper dialog must keep focus inside while open, put it back when closed, close on Escape and be announced correctly by assistive technology; a proper dropdown needs arrow-key navigation, type-to-select and sensible placement near the edges of the viewport. Most teams underestimate that work and ship something subtly broken.
Radix is the foundation shadcn/ui builds on, and Headless UI is maintained by the Tailwind team. The obvious cost is that you write all of the styling yourself, which is the whole point and still real work.
PrimeReact: breadth for unusual components
PrimeReact (v11.1.0 at the time of writing) belongs to the PrimeFaces family, which also covers Angular, Vue and Java:
npm install primereact
What it offers is sheer range, from charts and org charts to tree tables, schedulers, upload widgets and a complete set of inputs, many of which other libraries omit entirely. When you need something unusual, it is worth checking here first.
Verify its licensing yourself before committing. Its npm metadata points to a license file ("SEE LICENSE IN LICENSE.md") instead of giving an SPDX identifier, and the vendor also offers paid themes and templates next to the free package. The core library is open source, but read the actual terms before building a commercial product on it.
Three more worth a look
- Chakra UI (v3.37.0, MIT) puts accessibility first and styles components through props; it sits somewhere between the all-in approach of MUI and the bare primitives of Radix.
- daisyUI (v5.7.34, MIT) is a Tailwind plugin that provides component classes instead of React components, which means it is usable from Svelte, Vue or static markup too.
- HeroUI (v3.2.4, MIT), the renamed successor to NextUI, combines Tailwind with React Aria.
A quick decision guide
For the desktop shell:
- A proven ecosystem, identical rendering everywhere and a JavaScript-only team point to Electron.
- Small downloads, low memory, a stricter security model or a mobile target point to Tauri.
- A thin wrapper around an existing web UI points to Neutralino.
- Wanting Node and the DOM in one shared context points to NW.js.
For the component library:
- Needing nearly every component plus great documentation points to MUI.
- Wanting to own and change the code in a Tailwind project points to shadcn/ui.
- Dense enterprise data screens point to Ant Design.
- Good defaults without strong opinions point to Mantine.
- A custom design system with accessibility handled for you points to Radix or Headless UI.
- An unusual, specialized component points to PrimeReact.
One rule overrides the whole list: if your team already knows one of these tools well, that expertise almost always outweighs a marginally better fit elsewhere.
Wrapping up
Across both layers, feature lists have largely converged; these projects have had years to copy each other's good ideas. Weight is where they still differ in ways users feel. Electron versus Tauri poses the weight question for the whole application: bundle a browser and gain predictability, or lean on the one the OS provides for a bundle that can be twenty or more times smaller. Choosing between MUI and shadcn/ui applies that trade-off to the component layer: depend on a full design system maintained by others, or vendor just the pieces you need and maintain them yourself. Decide which side of that trade-off your product sits on before you start comparing feature tables, and the shortlist usually picks itself.