Production-ready starter for building desktop apps with Electron, Vite, TypeScript, React 18, and Fluent UI v9.
Replace the above placeholders with real screenshots by following docs/screenshot-guide.md.
- Electron 37 with secure preload (contextBridge) and IPC example
- Vite 7 for instant HMR in the renderer and fast builds
- React 18 + Fluent UI React Components v9 (Web)
- TypeScript across main, preload, and renderer
- Router, layout, and example pages wired up
- Vitest unit tests, ESLint + Prettier, and typechecking scripts
- Packaging via electron-builder for macOS (DMG/ZIP), Windows (MSI/NSIS/APPX/ZIP), and Linux (AppImage/DEB/ZIP)
- macOS ad‑hoc code signing afterPack so builds run out of the box locally
- Node.js 18+ (LTS recommended)
- macOS: Xcode Command Line Tools for codesign and DMG creation
- Windows: For APPX you need a developer certificate; MSI/NSIS are handled by electron-builder
- Linux: Platform packaging tools as needed by electron-builder targets
- Install dependencies
npm install
- Start in development
npm run dev
What happens:
- Vite serves the renderer on http://127.0.0.1:7777 with HMR
- vite-plugin-electron builds main and preload in watch mode and starts Electron
- On renderer changes the UI hot-reloads; on preload changes the window reloads; main changes restart the app
- Build for production (renderer + main + preload)
npm run build
- Package a desktop app (per your OS)
npm run package
Artifacts are placed in the release/ folder. On macOS, an ad‑hoc codesign is applied so the app opens without a developer certificate. See Packaging notes below.
- dev: Start Vite + Electron with live reload
- build: Production build for renderer, main, and preload
- package: Package with electron-builder (uses electron-builder.config.js)
- package:publish: Package and publish (requires configuration)
- lint: ESLint over the repo
- prettier:check / prettier:write: Verify or apply formatting for src/*/.ts,tsx
- typecheck, typecheck:main|preload|renderer: Run TS checks with no emit
- test, test:watch, test:coverage: Run unit tests with Vitest
src/
main/ # Electron main process (entry: index.ts)
preload/ # Secure preload (contextBridge API)
renderer/ # React + Fluent UI app powered by Vite
components/ # Layout, nav, error boundary
pages/ # Example routes: dashboard, inbox, settings, etc.
router/ # React Router (hash router)
styles/ # Global CSS
common/ # Shared types and utilities across processes
dist-*/ # Build outputs for each part (generated)
Key config files:
- vite.config.mts: Multi-entry Vite config for renderer, main, and preload (via vite-plugin-electron)
- electron-builder.config.js: Cross-platform packaging targets
- src/main/index.ts: Creates BrowserWindow, loads Vite dev server in dev or dist-renderer in prod
The main process loads either the dev server or the built HTML based on VITE_DEV_SERVER_URL:
// src/main/index.ts
if (process.env.VITE_DEV_SERVER_URL) {
browserWindow.loadURL(process.env.VITE_DEV_SERVER_URL);
} else {
browserWindow.loadFile(path.join(__dirname, "..", "dist-renderer", "index.html"));
}Vite dev server is configured at 127.0.0.1:7777 in vite.config.mts.
Only a typed, minimal API is exposed to the renderer through the preload script:
// src/preload/index.ts
contextBridge.exposeInMainWorld("ContextBridge", {
onNativeThemeChanged: (cb) => ipcRenderer.on("nativeThemeChanged", cb),
themeShouldUseDarkColors: () => ipcRenderer.sendSync("themeShouldUseDarkColors"),
});Type safety for window.ContextBridge is provided by src/renderer/window.d.ts and src/common/ContextBridge.ts.
Use it in React code:
useEffect(() => {
const refresh = () => {
const isDark = window.ContextBridge.themeShouldUseDarkColors();
// update your app theme state here
};
refresh();
window.ContextBridge.onNativeThemeChanged(() => refresh());
}, []);The renderer uses a hash router with a top bar and a left navigation sidebar. Pages are mounted under /#/ (e.g., /#/dashboard, /#/settings). See src/renderer/router and src/renderer/components/Layout.
- Command: npm run build && npm run package
- Output: release/ directory with platform-specific artifacts
- macOS: afterPack runs build/macos/codeSign.mjs to perform ad‑hoc signing so the app launches. For distribution signing and notarization, replace or remove this step and configure proper signing in electron-builder.
- Windows: Targets include MSI, NSIS, ZIP, and APPX. APPX requires a valid certificate and publisher info. Update electron-builder.config.js accordingly.
- Linux: AppImage, DEB, and ZIP targets are configured. Ensure necessary system tools are installed.
Tip: You can propagate your package.json version into the built app’s metadata using an environment variable:
export VITE_APP_VERSION=$(node -p "require('./package.json').version")
npm run package
On Windows PowerShell, use $env:VITE_APP_VERSION instead.
npm run lint
npm run typecheck
npm run test
Vitest example tests live in src/common.
- Electron window doesn’t open in dev: Ensure port 7777 isn’t in use; try npm run dev again.
- Preload changes don’t seem to apply: The renderer will auto-reload on preload builds; if not, quit and re-run dev.
- macOS “damaged” or “unverified developer”: Ad‑hoc signed local builds should open; for distribution, configure codesign and notarization.
- Windows APPX install issues: Enable Developer Mode or use NSIS/MSI targets.
Copyright © 2025 Kaan Karahanlı. This project is GPL-3.0 licensed.
