This document outlines the standards and best practices for developing desktop applications using Electron.js at Bayat.
- Project Structure
- Application Architecture
- Security Best Practices
- Performance Optimization
- IPC Communication
- Native Integration
- Packaging and Distribution
- Testing
- OS-Specific Considerations
- Resources
project-root/
├── .github/ # GitHub workflows and templates
├── assets/ # Application assets (icons, images)
├── build/ # Build configuration and scripts
├── dist/ # Distribution output
├── node_modules/ # Dependencies (git-ignored)
├── src/ # Source code
│ ├── main/ # Main process code
│ │ ├── index.ts # Main entry point
│ │ ├── menu.ts # Application menu
│ │ └── preload.ts # Preload scripts
│ ├── renderer/ # Renderer process code
│ │ ├── components/ # UI components
│ │ ├── hooks/ # Custom React hooks (if using React)
│ │ ├── pages/ # Application pages/screens
│ │ ├── store/ # State management
│ │ ├── utils/ # Utility functions
│ │ ├── App.tsx # Main React component (if using React)
│ │ └── index.tsx # Renderer entry point
│ └── common/ # Shared code between processes
│ ├── constants.ts # Shared constants
│ ├── types.ts # TypeScript types/interfaces
│ └── utils.ts # Shared utility functions
├── .eslintrc.js # ESLint configuration
├── .gitignore # Git ignore file
├── electron-builder.yml # Electron builder configuration
├── package.json # Project manifest
├── tsconfig.json # TypeScript configuration
└── README.md # Project documentation
- package.json: Define main process entry point and dependencies
- electron-builder.yml: Configure builds for different platforms
- src/main/index.ts: Main process entry point
- src/main/preload.ts: Preload scripts for secure IPC
- src/renderer/index.tsx: Renderer process entry point
- Maintain a clear separation between main and renderer processes
- Follow the principle of least privilege
- Main Process: Handle system-level operations (file I/O, native APIs)
- Renderer Process: Handle UI and user interactions
- Preload Scripts: Bridge main and renderer processes securely
- Use Redux, MobX, or Zustand for complex state management
- Avoid global state patterns that cross process boundaries
- Prefer local state for UI components when possible
- Use React, Vue, or Svelte for the renderer process
- TypeScript is strongly recommended for both processes
- Consider electron-react-boilerplate or vite-electron-builder for project startup
- Never enable
nodeIntegration
in renderer processes - Always use a preload script with contextIsolation
- Never use
allowRunningInsecureContent
orwebSecurity: false
- Keep Electron and Chromium up-to-date
- Validate all inputs from users and external sources
// Preload script (preload.ts)
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electronAPI', {
saveFile: (content: string) => ipcRenderer.invoke('save-file', content),
readFile: (path: string) => ipcRenderer.invoke('read-file', path)
})
// Main process (main.ts)
ipcMain.handle('save-file', async (event, content) => {
// Validate content
if (typeof content !== 'string') {
throw new Error('Invalid content type')
}
// Process the request
try {
// Save file logic
return { success: true }
} catch (error) {
console.error('Error saving file:', error)
return { success: false, error: error.message }
}
})
Implement a strict Content Security Policy to prevent XSS attacks:
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"default-src 'self'; script-src 'self'; object-src 'none'"
]
}
})
})
- Avoid heavy computation in the main process
- Use worker threads for CPU-intensive tasks
- Implement proper error handling to prevent crashes
- Implement code-splitting for large applications
- Optimize DOM operations and React rendering cycles
- Use virtual scrolling for large lists
- Watch for memory leaks, especially in event listeners
- Implement proper cleanup when windows are closed
- Use DevTools Memory profiler to identify issues
- Use
ipcRenderer.invoke
andipcMain.handle
for request/response patterns - Use
webContents.send
andipcRenderer.on
for one-way notifications
- Avoid using the remote module (deprecated and insecure)
- Don't use synchronous IPC methods that block UI
- Don't expose entire Node.js modules to the renderer
- Keep Node.js code in the main process whenever possible
- Use IPC to communicate between renderer and Node.js functionality
- Document all native module dependencies
- Prefer pure JS/TS modules when available
- Use node-gyp compatible modules
- Test native modules across all target platforms
- Use electron-builder for packaging
- Sign your applications for all platforms
- Implement auto-updates using electron-updater
# electron-builder.yml
appId: io.bayat.appname
productName: AppName
directories:
output: dist
buildResources: build
files:
- from: .
filter:
- package.json
- app
publish:
provider: github
owner: bayat
repo: appname
mac:
category: public.app-category.developer-tools
hardenedRuntime: true
gatekeeperAssess: false
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
win:
target:
- target: nsis
arch:
- x64
linux:
target:
- target: AppImage
- target: deb
category: Development
Implement auto-updates using electron-updater:
import { autoUpdater } from 'electron-updater'
export function setupAutoUpdater() {
autoUpdater.logger = log
autoUpdater.checkForUpdatesAndNotify()
}
- Unit Tests: Jest for individual functions
- Integration Tests: Spectron for application testing
- E2E Tests: Playwright or Cypress
// Jest unit test example
import { calculateValue } from '../src/common/utils'
describe('Utils', () => {
test('calculateValue should return correct result', () => {
expect(calculateValue(10, 5)).toBe(15)
})
})
- Implement proper app installation/uninstallation
- Support both light and dark themes
- Use native file dialogs
- Follow macOS UI guidelines
- Support sandboxing for Mac App Store
- Implement proper notarization for distribution
- Package as AppImage, deb, and rpm
- Follow freedesktop.org standards
- Test on multiple distributions (Ubuntu, Fedora)