Beginner-Friendly Electron API Demo Projects with Source CodeElectron lets web developers build cross-platform desktop apps using familiar web technologies: HTML, CSS, and JavaScript. For beginners, small demo projects that showcase Electron’s core APIs are the fastest way to learn how desktop features integrate with browser code. This article presents a set of approachable, well-explained demo projects, each focused on a specific Electron API or pattern, with clear requirements, implementation outlines, and links to source-code snippets you can copy and adapt.
Why use demo projects to learn Electron
Demo projects reduce cognitive load by isolating one concept at a time. Instead of reading abstract API docs, you see how code is structured, how the main and renderer processes communicate, how native dialogs work, and how packaging differs from web builds. Each demo below includes:
- Purpose and learning goals
- Required Electron version and deps
- Project structure and key files
- Step-by-step implementation notes
- Complete source-code snippets for core files
- Testing and packaging tips
Prerequisites and setup
Before starting, install Node.js (LTS recommended) and a code editor. Initialize a project folder for each demo or use a monorepo structure. Commands below assume npm; replace with yarn/pnpm if preferred.
Basic global commands:
node -v npm init -y npm install --save-dev electron@latest
Add a start script to package.json:
"scripts": { "start": "electron ." }
All demos use a simple main process entry (main.js or main.ts) and an HTML renderer (index.html + renderer.js). Use contextIsolation: true and preload scripts where appropriate for security.
Demo 1 — Basic Window & Menu (app, BrowserWindow, Menu)
Purpose: Learn how to create a native application window, customize its size and behavior, and add a native menu.
Learning goals:
- Create BrowserWindow with options
- Use app lifecycle events (ready, window-all-closed)
- Build a native Menu and MenuItem
Key files: main.js, index.html, renderer.js
main.js (core snippet):
const { app, BrowserWindow, Menu } = require('electron'); const path = require('path'); function createWindow() { const win = new BrowserWindow({ width: 900, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true } }); win.loadFile('index.html'); const template = [ { label: 'File', submenu: [{ role: 'quit' }] }, { label: 'View', submenu: [{ role: 'reload' }, { role: 'toggledevtools' }] } ]; const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); } app.whenReady().then(createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
Notes:
- On macOS set application menu differently; include app name.
- Add keyboard shortcuts with accelerator fields.
Demo 2 — Native Dialogs and File I/O (dialog, fs)
Purpose: Show how to open native file dialogs and read/write files.
Learning goals:
- Use dialog.showOpenDialog, dialog.showSaveDialog
- Read and write files securely via main process
- Communicate between renderer and main using IPC
Key files: main.js, preload.js, renderer.js, index.html
Preload exposes safe API:
const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { openFile: () => ipcRenderer.invoke('dialog:openFile'), saveFile: (data) => ipcRenderer.invoke('dialog:saveFile', data) });
Main process handlers:
const { ipcMain, dialog } = require('electron'); const fs = require('fs').promises; ipcMain.handle('dialog:openFile', async () => { const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'] }); if (canceled) return null; const content = await fs.readFile(filePaths[0], 'utf8'); return { path: filePaths[0], content }; }); ipcMain.handle('dialog:saveFile', async (event, { defaultPath, content }) => { const { canceled, filePath } = await dialog.showSaveDialog({ defaultPath }); if (canceled) return null; await fs.writeFile(filePath, content, 'utf8'); return { path: filePath }; });
Renderer usage example:
<button id="open">Open</button> <script> document.getElementById('open').addEventListener('click', async () => { const file = await window.electronAPI.openFile(); if (file) { document.body.innerText = file.content; } }); </script>
Notes:
- Use async/await and try/catch for error handling.
- Validate file paths and content sizes before loading.
Demo 3 — System Tray & Notifications (Tray, Notification)
Purpose: Demonstrate background persistence, tray icons, context menus, and native notifications.
Learning goals:
- Create a Tray icon with context menu
- Show native notifications
- Toggle app visibility from tray
Main process snippet:
const { Tray, Menu, Notification } = require('electron'); let tray; app.whenReady().then(() => { tray = new Tray(path.join(__dirname, 'icon.png')); const contextMenu = Menu.buildFromTemplate([ { label: 'Show', click: () => mainWindow.show() }, { label: 'Hide', click: () => mainWindow.hide() }, { type: 'separator' }, { label: 'Quit', role: 'quit' } ]); tray.setToolTip('Demo App'); tray.setContextMenu(contextMenu); const notif = new Notification({ title: 'Hello', body: 'Tray demo running' }); notif.show(); });
Notes:
- Use appropriate icon sizes for platforms.
- On macOS, Dock behavior differs; consider app.dock.hide().
Demo 4 — Auto Updates (autoUpdater) — Conceptual demo
Purpose: Introduce the auto-update workflow and how to integrate it.
Learning goals:
- Check for updates, download and notify user
- Handle update events and restart to install
Notes:
- For production use, set up a release server (GitHub Releases, electron-updater + electron-builder, or custom). This demo provides stubbed event flows you can test locally.
Example usage with electron-updater:
const { autoUpdater } = require('electron-updater'); autoUpdater.on('update-available', () => mainWindow.webContents.send('update:available')); autoUpdater.on('update-downloaded', () => { autoUpdater.quitAndInstall(); }); app.whenReady().then(() => { autoUpdater.checkForUpdatesAndNotify(); });
Important: auto-updates require code signing on macOS and Windows installer setup.
Demo 5 — Native Modules & Child Processes (child_process, native integrations)
Purpose: Show how to run CLI tools and native modules from Electron.
Learning goals:
- Use child_process.spawn/exec safely
- Stream output to renderer
- Handle long-running tasks without freezing UI
Main process example:
const { spawn } = require('child_process'); const proc = spawn('ping', ['-c', '4', 'example.com']); proc.stdout.on('data', (data) => { mainWindow.webContents.send('process:stdout', data.toString()); }); proc.on('close', (code) => { mainWindow.webContents.send('process:exit', code); });
Security notes:
- Avoid executing untrusted input directly.
- Use argument arrays and validate inputs.
Security best practices (short)
- Use contextIsolation: true and a preload script to expose minimal APIs.
- Avoid enabling nodeIntegration in renderer unless absolutely necessary.
- Validate and sanitize all IPC messages.
- Sign and notarize apps for macOS; use code signing for Windows installers.
Packaging and distribution (short)
- Use electron-builder or electron-forge to create platform installers.
- Test on each target OS and CI.
- Include assets and native dependencies in build config.
Suggested learning path
- Start with Demo 1 to understand app lifecycle and window basics.
- Move to Demo 2 to learn secure file access via IPC.
- Explore Demo 3 to handle background behavior and user notifications.
- Study Demo 5 for integrating external processes.
- Implement auto-updates in a staging environment before production.
If you want, I can: provide full, copy-pasteable project repos for any single demo, convert these to TypeScript, or generate a step-by-step tutorial with screenshots. Which would you like?
Leave a Reply