Electron scaffold
Skill secondorderai/ouroboros/.agents/skills/electron-scaffold
Ouroboros is an Agent Harness, CLI and Desktop App with Self-Improvement layer
npx -y skills add secondorderai/ouroboros --skill electron-scaffoldAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Scaffold a native-looking, effective Electron app with best practices baked in. Creates a production-ready Electron application with security hardening, modern tooling, proper IPC patterns, auto-updates, native UI elements, and optimal build configuration. Use this skill when users want to start a new Electron project or modernize an existing one.
SKILL.md
16.9 KB, as published. Nobody here has run it
Electron Application Scaffolding
Create production-ready Electron applications with security, performance, and native platform integration best practices built in from the start.
When to Use This Skill
Use this skill when:
- User wants to create a new Electron desktop application
- User needs to scaffold an Electron project with modern best practices
- User wants a native-looking cross-platform desktop app
- User mentions "Electron app", "desktop app", "cross-platform app", or similar
- User wants to modernize an existing Electron project structure
Prerequisites Check
Before scaffolding, verify:
-
Node.js: Check Node.js version (18.x or higher recommended)
node --version -
npm or yarn: Verify package manager is available
npm --version -
Git: Ensure git is available for version control
git --version
Architecture Decision Points
1. Build Tooling Choice
Ask the user which build system they prefer (or recommend based on use case):
Electron Forge (Recommended for most projects)
- All-in-one tooling solution
- Built-in TypeScript support
- Easy plugin system
- Great for: Most new projects, teams wanting batteries-included setup
Electron Builder
- Highly configurable
- Excellent multi-platform packaging
- Auto-update support
- Great for: Complex build requirements, specific packaging needs
Vite + Electron
- Fastest development experience
- Modern ESM-first approach
- Hot module replacement
- Great for: Modern web frameworks (React, Vue, Svelte), speed-focused development
2. Frontend Framework
Determine the UI framework:
- Vanilla JS/TypeScript: Lightest, full control
- React: Most popular, large ecosystem
- Vue: Progressive, easy to learn
- Svelte: Smallest bundle, compile-time framework
- Angular: Enterprise-ready, opinionated
3. TypeScript vs JavaScript
Strongly recommend TypeScript for:
- Better IDE support and autocomplete
- Catch errors at compile time
- Better maintainability
- Electron API typing support
Workflow
Step 1: Project Initialization
Based on tooling choice, initialize the project:
For Electron Forge (Recommended):
npm init electron-app@latest <app-name> -- --template=webpack-typescript
For Vite + Electron:
npm create @quick-start/electron <app-name>
For custom setup: Create package.json with proper dependencies (see templates).
Step 2: Project Structure Setup
Create a well-organized project structure:
<app-name>/
├── src/
│ ├── main/ # Main process
│ │ ├── main.ts # Entry point
│ │ ├── ipc/ # IPC handlers
│ │ ├── menu.ts # Native menu
│ │ └── tray.ts # System tray (if needed)
│ ├── preload/ # Preload scripts
│ │ └── preload.ts # Context bridge
│ ├── renderer/ # Renderer process
│ │ ├── index.html
│ │ ├── index.ts
│ │ └── styles/
│ └── shared/ # Shared types/utilities
│ └── types.ts
├── assets/ # Icons, images
├── resources/ # Build resources
├── dist/ # Build output
├── package.json
├── tsconfig.json
└── electron-builder.yml # or forge.config.js
Step 3: Security Configuration
CRITICAL: Implement security best practices from the start.
1. BrowserWindow Security Options:
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
// Security: Use preload scripts instead of nodeIntegration
nodeIntegration: false,
// Security: Isolate context between web content and preload
contextIsolation: true,
// Security: Disable remote module
enableRemoteModule: false,
// Security: Use sandboxed renderer
sandbox: true,
// Preload script for safe IPC
preload: path.join(__dirname, 'preload.js'),
// Security: Disable web security only in development if needed
webSecurity: true,
// Security: Disable navigation
allowRunningInsecureContent: false,
},
});
2. Content Security Policy (CSP):
// In main process or HTML meta tag
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"default-src 'self'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"connect-src 'self'",
].join('; '),
},
});
});
3. Context Bridge (preload.ts):
import { contextBridge, ipcRenderer } from 'electron';
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('electronAPI', {
// Invoke pattern (request-response)
getAppVersion: () => ipcRenderer.invoke('app:get-version'),
// Send pattern (one-way)
logMessage: (message: string) => ipcRenderer.send('log:message', message),
// Listener pattern (for receiving events)
onUpdateAvailable: (callback: (info: any) => void) => {
ipcRenderer.on('update:available', (_event, info) => callback(info));
},
// Remove listener
removeUpdateListener: () => {
ipcRenderer.removeAllListeners('update:available');
},
});
4. IPC Security Pattern:
// main/ipc/handlers.ts
import { ipcMain } from 'electron';
// Use invoke/handle pattern for request-response
ipcMain.handle('app:get-version', async () => {
return app.getVersion();
});
// Validate and sanitize all inputs
ipcMain.handle('file:read', async (_event, filePath: string) => {
// Validate path is within allowed directories
const allowedDir = app.getPath('userData');
const resolvedPath = path.resolve(filePath);
if (!resolvedPath.startsWith(allowedDir)) {
throw new Error('Access denied');
}
return fs.readFile(resolvedPath, 'utf-8');
});
Step 4: Native UI Elements
Create native-looking UI components:
1. Application Menu:
// main/menu.ts
import { Menu, shell } from 'electron';
export function createApplicationMenu(mainWindow: BrowserWindow) {
const template: MenuItemConstructorOptions[] = [
{
label: 'File',
submenu: [
{
label: 'New',
accelerator: 'CmdOrCtrl+N',
click: () => mainWindow.webContents.send('file:new'),
},
{ type: 'separator' },
{ role: 'quit' },
],
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
],
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
],
},
{
label: 'Help',
submenu: [
{
label: 'Learn More',
click: async () => {
await shell.openExternal('https://electronjs.org');
},
},
],
},
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
2. System Tray (Optional):
// main/tray.ts
import { Tray, Menu, nativeImage } from 'electron';
export function createTray(mainWindow: BrowserWindow) {
const icon = nativeImage.createFromPath(
path.join(__dirname, '../assets/tray-icon.png')
);
const tray = new Tray(icon);
const contextMenu = Menu.buildFromTemplate([
{
label: 'Show App',
click: () => {
mainWindow.show();
},
},
{
label: 'Quit',
click: () => {
app.quit();
},
},
]);
tray.setContextMenu(contextMenu);
tray.setToolTip('My Electron App');
return tray;
}
Step 5: Development Environment Setup
1. Hot Reload Configuration:
// main.ts
const isDevelopment = process.env.NODE_ENV === 'development';
if (isDevelopment) {
mainWindow.loadURL('http://localhost:5173'); // Vite dev server
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
}
2. Package.json Scripts:
{
"scripts": {
"dev": "concurrently \"npm:dev:*\"",
"dev:vite": "vite",
"dev:electron": "electron .",
"build": "npm run build:renderer && npm run build:main",
"build:renderer": "vite build",
"build:main": "tsc -p tsconfig.main.json",
"package": "electron-builder",
"package:all": "electron-builder -mwl",
"lint": "eslint src --ext .ts,.tsx",
"typecheck": "tsc --noEmit"
}
}
Step 6: Auto-Update Configuration
Implement automatic updates using electron-updater:
1. Install Dependencies:
npm install electron-updater
2. Update Configuration:
// main/updater.ts
import { autoUpdater } from 'electron-updater';
export function setupAutoUpdater(mainWindow: BrowserWindow) {
// Configure update server
autoUpdater.setFeedURL({
provider: 'github',
owner: 'your-username',
repo: 'your-repo',
});
// Check for updates on startup
autoUpdater.checkForUpdatesAndNotify();
// Update events
autoUpdater.on('update-available', (info) => {
mainWindow.webContents.send('update:available', info);
});
autoUpdater.on('update-downloaded', (info) => {
mainWindow.webContents.send('update:downloaded', info);
});
autoUpdater.on('error', (err) => {
mainWindow.webContents.send('update:error', err);
});
}
3. electron-builder Configuration:
# electron-builder.yml
appId: com.yourcompany.yourapp
productName: YourApp
directories:
output: dist
buildResources: resources
files:
- src/**/*
- package.json
mac:
category: public.app-category.productivity
target:
- dmg
- zip
hardenedRuntime: true
gatekeeperAssess: false
entitlements: resources/entitlements.mac.plist
win:
target:
- nsis
- portable
publisherName: Your Company
linux:
target:
- AppImage
- deb
category: Utility
publish:
provider: github
owner: your-username
repo: your-repo
Step 7: TypeScript Configuration
tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./src",
"types": ["node", "electron"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Step 8: Build and Package
Development:
npm run dev
Production Build:
npm run build
npm run package
Multi-platform Build:
npm run package:all
Best Practices Checklist
When scaffolding, ensure these are implemented:
Security
- Context isolation enabled
- Node integration disabled in renderer
- Preload script with context bridge
- Content Security Policy configured
- Input validation on all IPC handlers
- Sandbox mode enabled
- Web security enabled
- Navigation and redirect guards
Performance
- Lazy loading for renderer modules
- Background throttling configured
- Memory management for large datasets
- Efficient IPC patterns (batch updates)
- Webpack/Vite optimization
User Experience
- Native application menu
- Keyboard shortcuts (accelerators)
- Window state persistence
- Proper icon set (all sizes)
- Splash screen (optional)
- Error boundaries
- Loading states
Developer Experience
- TypeScript configured
- Hot reload working
- DevTools available in development
- ESLint and Prettier setup
- Git hooks with Husky (optional)
- Source maps enabled
Distribution
- Auto-update configured
- Code signing setup (platform-specific)
- Build scripts for all platforms
- Proper app metadata
- License file included
Error Handling Patterns
Main Process Errors
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// Log to file or error tracking service
app.quit();
});
process.on('unhandledRejection', (reason) => {
console.error('Unhandled Rejection:', reason);
});
Renderer Process Errors
window.addEventListener('error', (event) => {
window.electronAPI.logError({
message: event.error.message,
stack: event.error.stack,
});
});
window.addEventListener('unhandledrejection', (event) => {
window.electronAPI.logError({
message: 'Unhandled Promise Rejection',
reason: event.reason,
});
});
Platform-Specific Considerations
macOS
- Use
.icnsicon format - Implement dock menu
- Handle
activateevent (reopen window) - Consider macOS-specific menu items (About, Preferences)
- Code signing required for distribution
Windows
- Use
.icoicon format - Handle Squirrel startup events
- Consider Windows toast notifications
- NSIS installer customization
- Code signing with certificate
Linux
- Use
.pngicon format - Provide
.desktopfile - Handle different package formats (deb, AppImage, snap)
- Test on multiple distributions
Example Scaffolds
Minimal TypeScript Setup
# Initialize with Forge
npm init electron-app@latest my-app -- --template=webpack-typescript
# Add security defaults
# Add IPC patterns
# Configure build
React + TypeScript + Vite
# Create with Vite template
npm create @quick-start/electron my-app -- --template react-ts
# Add security hardening
# Configure auto-update
# Add native menus
Production-Ready Full Setup
- Complete security configuration
- Auto-update with GitHub releases
- Native UI elements (menu, tray)
- Error tracking
- Analytics (optional)
- Crash reporting
- Multi-platform build pipeline
Tips for Success
- Start Secure: Don't add security later—build it in from day one
- Type Everything: Use TypeScript for both main and renderer processes
- Test IPC Early: IPC issues are easier to debug early
- Platform Test: Test on all target platforms regularly
- Monitor Bundle Size: Keep renderer bundle optimized
- Document IPC Contract: Maintain API documentation between processes
- Version Control: Git ignore
dist/,node_modules/,.env - Use Process Manager: Handle main process crashes gracefully
- Implement Logging: Structured logging helps debug production issues
- Plan Updates: Design update strategy before first release
Common Pitfalls to Avoid
- ❌ Enabling
nodeIntegrationwithout good reason - ❌ Skipping context isolation
- ❌ Loading remote content without validation
- ❌ Exposing entire IPC renderer to web content
- ❌ Ignoring security warnings in console
- ❌ Not testing on all target platforms
- ❌ Hardcoding file paths (use
app.getPath()) - ❌ Forgetting to handle window state persistence
- ❌ Not implementing proper error boundaries
- ❌ Skipping code signing for distribution
Quick Start Command
For most users, recommend this command:
# Create app with TypeScript and Webpack
npm init electron-app@latest <app-name> -- --template=webpack-typescript
# Then apply security hardening, native UI, and build configuration
Reference Files
For detailed Electron API examples and configuration templates, see:
references/electron-security.md- Security best practicesreferences/ipc-patterns.md- IPC communication patternsreferences/build-config.md- Build and packaging configurationscripts/scaffold.sh- Automated scaffolding script
Post-Scaffold Checklist
After scaffolding, guide the user to:
- ✅ Review and customize
package.jsonmetadata - ✅ Add application icons (all platforms)
- ✅ Configure code signing certificates
- ✅ Set up GitHub repository for auto-updates
- ✅ Test hot reload and development workflow
- ✅ Build for all target platforms
- ✅ Test update mechanism
- ✅ Review security settings
- ✅ Add error tracking (Sentry, etc.)
- ✅ Create user documentation
Version Compatibility
This skill targets:
- Electron: v28+ (latest stable)
- Node.js: v18+ LTS
- TypeScript: v5+
- Electron Forge: v7+
- Electron Builder: v24+
Always check the latest Electron documentation for breaking changes.