Mohit's blog

2025 Setup Guide: Electron-Vite + Tailwind-Shadcn UI

Cover.webp
Published on
/
6 mins read
/
––– views

Building modern desktop apps no longer means wrestling with Webpack or hand-rolled CSS. With Electron ⚡ Vite you get an instant-reload development loop, Tailwind CSS v4 gives you atomic styling at lightspeed, and ShadCN UI sprinkles in production-ready components - all without leaving JavaScript. This post walks you through a clean, JavaScript-only setup, shows how to keep the ShadCN CLI happy with a jsconfig.json alias, and highlights the little “gotchas” that trip people up in 2025. By the end you’ll have a hot-reloading Electron window, styled with Tailwind 4 utility classes, and a ShadCN button proving everything works together.

Why pick this stack in 2025?

Tailwind CSS v4 introduced a brand-new engine that pares the runtime to almost nothing while adding goodies like native text-shadow utilities and mask support (tailwindcss.com, tailwindcss.com). Electron-Vite, meanwhile, moved to v3 with first-class support for React, Vue, and Svelte templates plus blazingly fast HMR in both the main and renderer processes (electron-vite.org, electron-vite.org). And ShadCN UI keeps shipping accessible, unstyled-by-default components that slot neatly into any Tailwind project, including Vite-powered Electron apps (ui.shadcn.com).

Put together, you get:

  • Instant startups: Vite serves your React renderer in milliseconds.
  • Atomic styling: Tailwind 4 scans your source and bakes just the classes you use.
  • Ready-made components: ShadCN UI’s headless design system accelerates delivery.

Prerequisites

ToolTested Version
Node≥ 20.11
npm / pnpm / yarnany
Gitlatest
VS Code (optional)for alias IntelliSense

Tip: If you’re on Windows, run PowerShell as administrator so native build steps can link correctly.

Step-by-Step Guide

1. Scaffold an Electron-Vite project

npm create electron-vite@latest vibe-layer
cd vibe-layer
npm install
npm run dev

The scaffolder pulls the official template from create-electron-vite, giving you separate main, preload, and renderer folders out of the box (github.com).

2. Install Tailwind CSS v4 for the renderer

npm install -D tailwindcss @tailwindcss/vite

Add the Tailwind plugin to vite.config.js (we’re staying JavaScript-only):

import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { resolve } from 'path'
 
export default defineConfig({
  main: { plugins: [externalizeDepsPlugin()] },
  preload: { plugins: [externalizeDepsPlugin()] },
  renderer: {
    resolve: {
      alias: {
        '@renderer': resolve('src/renderer/src'),
        '@': resolve('src/renderer/src'),
      },
    },
    plugins: [react(), tailwindcss()],
  },
})

or if you already have electron.vite.config.js, create a vite.config.js file and paste the contents of electron.vite.config.js inside it.

Finally, put @import "tailwindcss"; at the very top of src/renderer/src/assets/main.css so Tailwind generates its utilities in dev and build time (dev.to).

3. Verify Tailwind works

Add a throw-away line inside App.tsx/App.jsx:

<h1 className="text-3xl font-bold text-emerald-600">It works! 🎉</h1>

Hit Save and your Electron window should hot-reload with a green headline. If nothing happens, double-check that the renderer process imported main.css and that Tailwind post-CSS plugin logs to the terminal (stackoverflow.com, github.com).

4. Add a JavaScript alias

ShadCN’s CLI validates that @/* resolves to your source root. Because we’re using JavaScript, we create a jsconfig.json instead of tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/renderer/src/*"],
      "@renderer/*": ["src/renderer/src/*"]
    }
  }
}

Save it at project root. Your IDE now autocompletes @/components/Button.jsx and the CLI finds its alias without TypeScript noise (dev.to, gbuszmicz.medium.com).

5. Initialize ShadCN UI

npx shadcn@latest init

First run may complain that “a supported framework was not found.” ShadCN only looks for vite.config.* in the root, so copy your existing electron.vite.config.mjs to vite.config.js (or symlink it). Then rerun the command - this time it detects Vite and Tailwind and walks you through theme choices (dev.to, ui.shadcn.com).

6. Render your first ShadCN component

Inside App.jsx:

import { Button } from '@/components/ui/button'
 
export default function App() {
  return (
    <div className="flex h-screen items-center justify-center bg-slate-950">
      <Button variant="default">Hello from ShadCN inside Electron!</Button>
    </div>
  )
}

Save, and the familiar ShadCN button appears - proof the trio is wired up.

7. Build for production

npm run build

Electron-Vite bundles the main/preload with esbuild and the renderer with Vite’s roll-up pipeline, hashing assets automatically (github.com).

Common gotchas & quick fixes

SymptomCauseFix
No import alias found in your tsconfig.jsonRunning ShadCN CLI without a matching jsconfig/tsconfig entryEnsure "@/*": ["src/renderer/src/*"] exists in jsconfig.json (reddit.com)
Tailwind classes don’t show upForgot the @import "tailwindcss"; line or plugin isn’t loadedRe-add the import, restart dev server (github.com)
ESLint can’t resolve @/ESLint needs its own alias mappingAdd "settings": { "import/resolver": { "typescript": { "project": "./jsconfig.json" } } } to .eslintrc
Prod build ships 5 MB of iconsNo Tree-shaken ShadCN icon cause of import * as Iconsimports by using import { ArrowRight } from 'lucide-react' - use what is only required

Final thoughts

With less than a dozen commands you now have a 2025-ready Electron app that ships with Vite’s dev-experience, Tailwind CSS v4’s ultra-light engine, and ShadCN UI’s accessible components. The stack is entirely JavaScript-based, so no extra TypeScript plumbing is required - but you can add it later if static types become valuable.

Take the time to explore Tailwind’s new composable variants, experiment with ShadCN’s theming, and remember to keep your aliases in sync across Vite, jsconfig, and ESLint to prevent the only class of errors people still hit a year from now. Happy building! 🎉