Electron App Scaffolder
Creates a production-ready Electron application with TypeScript, React, Vite, and best practices.
Usage
/electron-app <app-name> [--with-db] [--with-tests] [--with-storybook]
Arguments
- •
app-name(required): Name of the application (kebab-case recommended) - •
--with-db: Include SQLite database with Knex.js migrations - •
--with-tests: Include Jest testing setup - •
--with-storybook: Include Storybook for component development
Instructions
When this skill is invoked, create a new Electron application following these steps:
1. Project Initialization
Create the project directory and initialize:
mkdir <app-name> cd <app-name> npm init -y
2. Directory Structure
Create the following structure:
<app-name>/ ├── electron/ │ ├── main.ts │ └── preload.ts ├── src/ │ ├── renderer/ │ │ ├── components/ │ │ ├── types/ │ │ │ └── electron.d.ts │ │ ├── utils/ │ │ ├── App.tsx │ │ ├── main.tsx │ │ └── index.css │ └── main/ ├── dist/ │ ├── main/ │ └── renderer/ ├── index.html ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── package.json
If --with-db is specified, also create:
├── migrations/
├── knexfile.js
└── scripts/
└── seed-db.js
If --with-tests is specified, also create:
├── jest.config.js ├── src/renderer/setupTests.ts └── __tests__/
If --with-storybook is specified, also create:
└── .storybook/
├── main.ts
└── preview.ts
3. Package.json Configuration
Update package.json with:
Core Dependencies:
- •
electron: Latest stable - •
react,react-dom: ^18.x - •
electron-builder: For distribution
Development Dependencies:
- •
typescript: ^5.x - •
vite: ^5.x - •
@vitejs/plugin-react: Latest - •
electron-builder: Latest - •
concurrently: For running dev servers - •
wait-on: For coordinating startup
If --with-db:
- •
better-sqlite3: ^9.x - •
knex: ^3.x - •
@faker-js/faker: For seeding
If --with-tests:
- •
jest,@types/jest - •
ts-jest - •
@testing-library/react,@testing-library/jest-dom
Scripts:
{
"scripts": {
"dev": "concurrently \"npm run dev:vite\" \"npm run dev:electron\"",
"dev:vite": "vite",
"dev:electron": "wait-on http://localhost:5173 && electron .",
"build": "tsc && vite build && electron-builder",
"build:mac": "npm run build -- --mac",
"build:win": "npm run build -- --win",
"build:linux": "npm run build -- --linux",
"lint": "eslint src --ext ts,tsx",
"test": "jest",
"test:watch": "jest --watch"
}
}
Add --with-db scripts if needed:
{
"scripts": {
"migrate": "knex migrate:latest",
"migrate:rollback": "knex migrate:rollback",
"seed": "node scripts/seed-db.js"
}
}
4. TypeScript Configuration
tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
tsconfig.node.json:
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"outDir": "dist/main"
},
"include": ["electron"]
}
5. Vite Configuration
vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
base: './',
build: {
outDir: 'dist/renderer',
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 5173,
},
});
6. Main Process (electron/main.ts)
Create a secure main process with:
- •BrowserWindow with
contextIsolation: trueandnodeIntegration: false - •IPC handlers for app operations
- •Development/production URL loading logic
- •If
--with-db: Database initialization and IPC handlers
Key patterns:
import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';
let mainWindow: BrowserWindow | null = null;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
}
}
app.whenReady().then(() => {
// Initialize database if --with-db
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// IPC Handlers
ipcMain.handle('app:getVersion', () => app.getVersion());
7. Preload Script (electron/preload.ts)
Create secure context bridge:
import { contextBridge, ipcRenderer } from 'electron';
export interface ElectronAPI {
getVersion: () => Promise<string>;
// Add more API methods here
}
contextBridge.exposeInMainWorld('electronAPI', {
getVersion: () => ipcRenderer.invoke('app:getVersion'),
} as ElectronAPI);
8. Renderer Type Definitions (src/renderer/types/electron.d.ts)
import type { ElectronAPI } from '../../../electron/preload';
declare global {
interface Window {
electronAPI: ElectronAPI;
}
}
export {};
9. React Application
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><app-name></title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/renderer/main.tsx"></script>
</body>
</html>
src/renderer/main.tsx:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
src/renderer/App.tsx:
import React, { useEffect, useState } from 'react';
function App() {
const [version, setVersion] = useState<string>('');
useEffect(() => {
window.electronAPI.getVersion().then(setVersion);
}, []);
return (
<div className="app">
<h1>Welcome to <app-name></h1>
<p>App version: {version}</p>
</div>
);
}
export default App;
10. Database Setup (if --with-db)
knexfile.js:
const path = require('path');
const { app } = require('electron');
const dbPath = app
? path.join(app.getPath('userData'), '<app-name>.db')
: path.join(__dirname, '<app-name>.db');
module.exports = {
client: 'better-sqlite3',
connection: {
filename: dbPath,
},
useNullAsDefault: true,
migrations: {
directory: './migrations',
},
};
Create initial migration:
npx knex migrate:make create_initial_tables
11. Testing Setup (if --with-tests)
jest.config.js:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.test.ts?(x)'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
setupFilesAfterEnv: ['<rootDir>/src/renderer/setupTests.ts'],
};
src/renderer/setupTests.ts:
import '@testing-library/jest-dom';
12. Electron Builder Configuration
Add to package.json:
{
"main": "dist/main/main.js",
"build": {
"appId": "com.<app-name>.app",
"productName": "<app-name>",
"files": [
"dist/**/*",
"package.json"
],
"directories": {
"output": "release"
},
"mac": {
"target": ["dmg"],
"category": "public.app-category.utilities"
},
"win": {
"target": ["nsis"]
},
"linux": {
"target": ["AppImage"],
"category": "Utility"
}
}
}
13. Git Ignore
Create .gitignore:
node_modules/ dist/ release/ *.log .DS_Store *.db *.db-shm *.db-wal
14. README
Create a README.md with:
- •Project description
- •Development setup instructions
- •Available npm scripts
- •Architecture overview
- •Build instructions
15. Final Steps
After scaffolding:
- •Run
npm installto install all dependencies - •If
--with-db: Runnpm run migrateto create database - •Inform user they can start development with
npm run dev - •List all available commands
- •Mention key files to customize
Security Best Practices
Ensure all generated code follows:
- •✅
contextIsolation: true - •✅
nodeIntegration: false - •✅ Use
contextBridgefor all IPC - •✅ Validate all IPC inputs in main process
- •✅ Never expose full Electron APIs to renderer
Success Criteria
The skill should create a fully functional Electron app that:
- •✅ Runs in development with hot reload
- •✅ Has proper TypeScript types throughout
- •✅ Uses secure IPC communication
- •✅ Can be built for production
- •✅ Follows modern Electron best practices
- •✅ Is well-documented and ready to extend