
Electron Best Practices
Scaffold a production-minded electron-vite setup with main, preload, and React renderer processes without juggling separate webpack configs.
Overview
electron-best-practices is an agent skill for the Build phase that provides a unified electron-vite configuration for main, preload, and React renderer processes.
Install
npx skills add https://github.com/jwynia/agent-skills --skill electron-best-practicesWhat is this skill?
- Unified electron-vite config for main, preload, and renderer using Vite
- React plugin in renderer with dedicated root and index.html entry
- externalizeDepsPlugin on main and preload for correct Node bundling
- Shared @shared path alias across processes
- Production terser minify, dev sourcemaps, and NODE_ENV-aware build toggles
- 3 Electron processes configured: main, preload, renderer
Adoption & trust: 635 installs on skills.sh; 92 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You want a desktop app with Electron but dread maintaining three different bundler configs and inconsistent aliases between main and renderer.
Who is it for?
Indie developers starting or hardening an Electron + React + TypeScript desktop product on electron-vite.
Skip if: Web-only SaaS, native mobile shells, or teams that already standardize on electron-forge without Vite.
When should I use this skill?
Invoke when bootstrapping or tuning electron-vite for a React desktop app with separate main, preload, and renderer entry points.
What do I get? / Deliverables
You get a copy-ready electron.vite.config.ts with externalized deps, React renderer wiring, shared aliases, and production minify/sourcemap policy.
- electron.vite.config.ts pattern with rollup inputs and aliases
- Environment-aware build defaults for production and development
Recommended Skills
Journey fit
Desktop app construction with Electron and Vite happens in Build when you are implementing the client shell and process boundaries. frontend covers renderer UX plus the cross-process config that solo builders treat as the visible product surface.
How it compares
Configuration template for electron-vite, not a full Ship-phase code-signing or auto-update checklist.
Common Questions / FAQ
Who is electron-best-practices for?
Solo builders creating Electron desktop apps who want Vite-powered HMR and one config file for all three processes.
When should I use electron-best-practices?
During Build frontend work when you are setting up or refactoring electron-vite entries, React renderer roots, and shared aliases before feature coding.
Is electron-best-practices safe to install?
It is documentation and config patterns only; still review the Security Audits panel on this page and audit any generated build scripts before CI publish.
SKILL.md
READMESKILL.md - Electron Best Practices
# electron-vite Configuration electron-vite provides a unified build configuration for all three Electron processes (main, preload, and renderer) using Vite under the hood. This eliminates the need for separate webpack or rollup configs and provides fast HMR during development. The configuration below demonstrates a production-ready setup with React in the renderer, proper path aliasing across processes, and environment-aware optimizations. ```typescript // electron.vite.config.ts import { defineConfig, externalizeDepsPlugin } from 'electron-vite'; import react from '@vitejs/plugin-react'; import { resolve } from 'path'; export default defineConfig({ main: { plugins: [externalizeDepsPlugin()], build: { rollupOptions: { input: resolve(__dirname, 'src/main/index.ts'), }, // Production optimizations minify: process.env.NODE_ENV === 'production' ? 'terser' : false, sourcemap: process.env.NODE_ENV !== 'production', }, resolve: { alias: { '@shared': resolve(__dirname, 'src/shared'), }, }, }, preload: { plugins: [externalizeDepsPlugin()], build: { rollupOptions: { input: resolve(__dirname, 'src/preload/index.ts'), }, sourcemap: process.env.NODE_ENV !== 'production', }, }, renderer: { plugins: [react()], root: resolve(__dirname, 'src/renderer'), build: { rollupOptions: { input: resolve(__dirname, 'src/renderer/index.html'), output: { manualChunks: { vendor: ['react', 'react-dom'], }, }, }, minify: process.env.NODE_ENV === 'production' ? 'terser' : false, sourcemap: process.env.NODE_ENV !== 'production', }, resolve: { alias: { '@': resolve(__dirname, 'src/renderer/src'), '@shared': resolve(__dirname, 'src/shared'), }, }, }, }); ``` ## Customization Notes ### externalizeDepsPlugin The `externalizeDepsPlugin()` is critical for the main and preload processes. It externalizes all Node.js dependencies so they are not bundled into the output -- they remain as `require()` calls resolved at runtime from `node_modules`. This avoids issues with native modules and keeps the bundle size small. Do **not** apply this plugin to the renderer process, which runs in a browser-like context and needs its dependencies bundled. ### Path Aliases The `@shared` alias is available in both the main process and the renderer, enabling shared type definitions, constants, and utility functions. The renderer additionally has `@` pointing to its own source root for cleaner imports. When adding aliases, ensure matching entries exist in the corresponding `tsconfig.json` paths to keep TypeScript and the bundler in sync. ### Manual Chunks The `manualChunks` configuration in the renderer output splits large vendor libraries (React, React DOM) into a separate chunk. This improves caching behavior -- your application code can change without invalidating the vendor bundle. Add additional entries for other large dependencies like state management libraries or UI component frameworks. ### Environment-Aware Builds Sourcemaps are enabled in development for debugging and disabled in production to reduce bundle size and avoid exposing source code. Minification via `terser` is only applied for production builds. If you prefer `esbuild` for faster minification at the cost of slightly larger output, replace `'terser'` with `'esbuild'`. ### Multiple Preload Scripts If your application uses multiple `BrowserWindow` instances each with a different preload script, expand the preload input to an object: ```typescript preload: { plugins: [externalizeDepsPlugin()], build: { rollupOptions: { input: { main: resolve(__dirname, 'src/preload/main.ts'), settings: resolve(__dirname, 'src/preload/settings.ts'), }, }, }, }, ``` ### Development Server Configuration electron-vite starts a