If you’ve ever wanted to build a desktop app using tools you already know from the web — React, Vite, Tailwind — you’re going to love this stack. I recently built a desktop tool using these exact technologies and it was smoother than I expected.
Here's the catch: use
npm
, notpnpm
, especially when you're working with Electron. This is important if you're just starting out, which applies to me and probably to you too.
Let’s break down why.
First reason: The Stack
We’re talking about:
- Electron – to create desktop apps with web tech
- React – for building UI
- Vite – for super-fast development
- Tailwind CSS – for styling with ease
- npm – to manage it all
You can spin all of this up in under 10 minutes. But the way you install and structure your dependencies matters. That’s where npm
vs pnpm
comes in.
Why you should use npm
?
It’s Familiar
If you've done anything with JavaScript, you’ve used npm
. The learning curve is basically zero.
It Plays Nice with Electron
Electron expects dependencies to be laid out in a certain way. When you package your Electron app for production, tools like electron-builder
scan your node_modules
to include required files.
With npm
, that's straightforward. The node_modules
folder is flat and easy to traverse.
But…
Why pnpm
Can Cause Headaches with Electron
pnpm
is great — it's fast, efficient, and deduplicates packages like a boss. But its performance gain comes from a symlinked folder structure, not the flat node_modules
you're used to.
That’s where the pain begins.
The Problem:
Electron builders (like electron-builder
) or runtime expectations often fail to properly resolve packages when they’re symlinked in the way pnpm
sets them up. You might get weird runtime errors like:
-
Cannot find module 'electron'
(i hate this one) Module not found: path-to-your-lib
- Your app builds but crashes on startup
Or even worse: everything works in dev, but breaks in production packaging
Unless you’re experienced with pnpm
overrides or ready to tinker with buildResources
, asarUnpack
, and manual packaging configs — stick with npm
to avoid that rabbit hole.
Okay, you convinced me to use npm. Now What?
You can scaffold the whole thing like this:
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm install --save-dev electron concurrently wait-on electron-builder
Set up your Electron main process (e.g. in electron/main.js
), add Tailwind, wire up your package.json
scripts:
"scripts": {
"dev": "concurrently \"vite\" \"npm:electron-dev\"",
"electron-dev": "wait-on http://localhost:5173 && electron .",
"build": "vite build && electron-builder"
}
Boom. You’re building desktop apps.
Final Thoughts
I really like pnpm
, it’s fast, smart, and a great choice for most projects. But if you’re using Electron, especially with tools like electron-builder
, do yourself a favor and stick with npm
until you're comfortable with Electron’s internals.
This combo — Electron + React + Vite + Tailwind + npm — is powerful, fast to set up, and surprisingly stable. Whether you’re building an internal tool, a personal project, or your next big product — it just works.
Top comments (0)