Rapid Prototyping with Electron API Demos: Practical Project Templates

Electron API Demos: Hands-On Examples for Building Desktop Apps

Building cross-platform desktop apps with Electron is fast and practical, but understanding its APIs makes the difference between a prototype and a production-quality app. This article walks through hands-on examples using core Electron APIs you’ll use most often: window creation, menus, IPC (main ↔ renderer), native dialogs, and system integrations like notifications and the tray. Each example includes a short goal, key code snippets, and quick notes on best practices.

Prerequisites

  • Node.js installed (LTS recommended)
  • Basic knowledge of JavaScript and npm
  • An existing Electron project scaffolded (npx create-electron-app or manual setup)

1) Create the main window (BrowserWindow)

Goal: Open a resizable app window and load your frontend.

Key code (main process: main.js):

javascript
const { app, BrowserWindow } = require(‘electron’); function createWindow() { const win = new BrowserWindow({ width: 1024, height: 768, webPreferences: { nodeIntegration: false, contextIsolation: true, preload: _dirname + ‘/preload.js’ } }); win.loadFile(‘index.html’);} app.whenReady().then(createWindow);app.on(‘window-all-closed’, () => { if (process.platform !== ‘darwin’) app.quit(); });

Notes: Use contextIsolation and a preload script to keep renderer code secure. Manage window state (size/position) via a small persistence layer if desired.

2) Native menus and accelerators

Goal: Add an application menu with keyboard shortcuts.

Key code (main process):

javascript
const { Menu } = require(‘electron’); const template = [ { label: ‘File’, submenu: [ { label: ‘Open’, accelerator: ‘CmdOrCtrl+O’, click: () => {/open logic */} }, { role: ‘quit’ } ] }, { role: ‘editMenu’ }, { role: ‘viewMenu’ }]; const menu = Menu.buildFromTemplate(template);Menu.setApplicationMenu(menu);

Notes: Use predefined roles for standard behavior across platforms. Localize labels where needed.

3) IPC: Secure main ↔ renderer communication

Goal: Exchange messages and data between renderer and main safely.

Preload (preload.js):

javascript
const { contextBridge, ipcRenderer } = require(‘electron’); contextBridge.exposeInMainWorld(‘api’, { send: (channel, data) => ipcRenderer.invoke(channel, data), receive: (channel, cb) => ipcRenderer.on(channel, (e, …args) => cb(…args))});

Main (main.js):

javascript
const { ipcMain, dialog } = require(‘electron’); ipcMain.handle(‘select-file’, async () => { const result = await dialog.showOpenDialog({ properties: [‘openFile’] }); return result.filePaths || [];});

Renderer (renderer.js):

javascript
async function chooseFile() { const paths = await window.api.send(‘select-file’); console.log(paths);}

Notes: Prefer ipcMain.handle/invoke for request/response patterns. Sanitize and validate any data before processing.

4) Native dialogs (open/save/alert)

Goal: Let users choose files or confirm actions using native UI.

Example (main process):

javascript
const { dialog } = require(‘electron’); ipcMain.handle(‘save-file’, async (, { defaultPath }) => { const { canceled, filePath } = await dialog.showSaveDialog({ defaultPath }); if (canceled) return null; return filePath;});

Notes: Always call dialogs from the main process. Use filters for file types.

5) Notifications and tray icon

Goal: Show system notifications and run a background tray icon with a menu.

Notification (renderer or main with permission):

javascript
new Notification({ title: ‘Sync Complete’, body: ‘All files uploaded.’ }).show();

Tray (main process):

javascript
const { Tray, Menu } = require(‘electron’);const tray = new Tray(‘assets/icon.png’);tray.setToolTip(‘My App’);tray.setContextMenu(Menu.buildFromTemplate([ { label: ‘Open’, click: () => {/* show window */} }, { label: ‘Quit’, role: ‘quit’ }]));

Notes: Respect platform differences (Windows/Linux/macOS) for icon sizes and notification behavior.

6) File system access, auto-updates, and native integration

Goal: Read/write files and keep your app updated.

  • Use Node fs in the main process or via secure IPC. Validate paths and never allow arbitrary writes without checks.
  • Use electron-updater or Squirrel (depending on platform) for auto-update flows; test updates on staging channels.
  • Integrate with OS features (deep links, protocol handlers, file associations) via app.setAsDefaultProtocolClient and relevant manifest settings.

7) Packaging and distribution

Goal: Prepare your app for users.

  • Use electron-builder or electron-forge to create platform-specific installers (DMG, EXE, AppImage).
  • Code-sign your app for macOS and Windows to avoid warnings and enable smooth updates.
  • Test on real OS versions (not just development machine).

Best practices summary

  • Security: enable contextIsolation, disable nodeIntegration in renderer, use a preload bridge, validate IPC inputs.
  • Performance: lazy-load heavy modules, avoid blocking the main process, move CPU work to worker threads if necessary.
  • Reliability: handle uncaught exceptions in main/renderer, persist user state, provide clear error reporting.
  • UX: follow platform conventions for menus, keyboard shortcuts, and window behavior.

Next steps (practical exercise)

  1. Scaffold an app with create-electron-app.
  2. Implement a BrowserWindow with a preload exposing a single file-picker API.
  3. Add a tray icon with a menu item that shows the window.
  4. Package the app with electron-builder and test on one other OS.

These examples give you a practical foundation to build, secure, and distribute real desktop apps with Electron. Use them as templates, expand each stub into features you need, and consult Electron docs for deeper API details.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *