A modern Electron + React starter built on Electron Forge and Vite, with a batteries-included TypeScript/tooling setup for shipping production desktop apps.
- Electron app with:
- Main process entry:
src/main.ts - Preload entry:
src/preload.ts - React renderer entry:
src/renderer.tsx(mounted intoindex.html)
- Main process entry:
- React 19 renderer (Vite +
@vitejs/plugin-react) - TypeScript across main/preload/renderer
- Electron Forge (
@electron-forge/cli) for lifecycle scripts:npm start→ dev (Forge + Vite dev server)npm run package→ packaged app (asar)npm run make→ platform installers/artifactsnpm run publish→ publish flow (requires your own publishing config/credentials)
- Electron Forge Vite plugin (
@electron-forge/plugin-vite) with separate Vite configs:vite.main.config.ts(main)vite.preload.config.ts(preload)vite.renderer.config.ts(renderer)
- ASAR enabled (
packagerConfig.asar: true) - Electron Fuses flipped at package time via
@electron/fuses(seeforge.config.ts) including:OnlyLoadAppFromAsar: trueEnableEmbeddedAsarIntegrityValidation: true- Cookie encryption enabled
- Disables Node CLI inspect/environment hooks in production
This keeps the default template aligned with shipping a hardened binary (while still allowing full dev ergonomics).
- ESLint v9 flat config (
eslint.config.ts) with:typescript-eslintstrict + type-aware ruleseslint-plugin-prettierto enforce Prettier formattingeslint-plugin-simple-import-sortfor deterministic import ordering
- Prettier (
.prettierrc.json) - Vitest (
vitest.config.ts) with example component tests undersrc/ui/*.test.tsx - Knip (
knip.json) to detect unused files/exports/dependencies
- release-it config in
package.json:- Runs
typecheck,lint, andpackagein release hooks - Creates GitHub releases
- Creates and pushes a
release/<version>branch after releasing
- Runs
- auto-changelog for changelog generation (
npm run changelog)
- Node version hints:
.nvmrc/.node-versionincluded (recommended local Node version)package.jsonrequires Node>=20
- VS Code workspace defaults in
.vscode/(recommended extensions + formatting/lint settings) - Lefthook installed (see
lefthook.ymland.lefthook/scripts) for optional Git hooks
.
├─ src/
│ ├─ main.ts # Electron main process
│ ├─ preload.ts # Preload script (secure IPC surface belongs here)
│ ├─ renderer.tsx # React entrypoint
│ ├─ index.css # Renderer styles
│ └─ ui/
│ ├─ App.tsx
│ ├─ Greeting.tsx
│ └─ *.test.tsx
├─ forge.config.ts
├─ vite.*.config.ts
├─ eslint.config.ts
└─ vitest.config.ts
- Node.js
>= 20(see.nvmrc/.node-versionfor the recommended version) - npm (or your preferred package manager; this repo uses npm scripts by default)
npm installnpm startThis launches Electron via Forge and loads the renderer from the Vite dev server.
# TypeScript (no emit)
npm run typecheck
# Lint (ESLint + Prettier + import sorting)
npm run lint
# Auto-fix lint/format issues where possible
npm run lint:fix
# Tests
npm test
# Unused files/exports/deps
npm run knip# Package the app (creates an unpacked packaged application)
npm run package
# Build distributables (platform-specific makers)
npm run makeMakers configured in forge.config.ts include:
- Windows: Squirrel
- macOS: ZIP
- Linux: DEB + RPM
Common starting points:
- App window behavior:
src/main.ts- The template currently opens DevTools by default (adjust as desired).
- Preload / IPC surface:
src/preload.ts- Prefer exposing a small, explicit API via
contextBridgeand keeping Node/Electron privileges out of the renderer.
- Prefer exposing a small, explicit API via
- Renderer UI:
src/ui/*andsrc/renderer.tsx - Product metadata:
package.json:name,productName,description,version
- Windows install/uninstall shortcut handling is enabled via
electron-squirrel-startup(seesrc/main.ts). - Electron fuses are flipped during packaging (see
forge.config.ts). If you change hardening settings, keep dev vs package behavior in mind.
BSD-3-Clause (see package.json).
Built for you with ❤️ on Bali! Find more great tools & templates on my GitHub Profile.