Table of Contents
- What is Electron?
- Why Choose Electron?
- Setting Up Your Development Environment
- Building Your First Electron App: A Step-by-Step Guide
- Core Concepts: Main vs. Renderer Processes
- Packaging and Distributing Your Electron App
- Advanced Features
- Best Practices for Electron Development
- Conclusion
- References
What is Electron?
Electron is a framework that enables developers to build cross-platform desktop applications using web technologies. At its core, it consists of three key components:
- Chromium: Renders the app’s user interface (UI) using HTML, CSS, and JavaScript, ensuring consistent rendering across platforms.
- Node.js: Provides access to the operating system’s file system, network, and other native APIs via JavaScript.
- Custom APIs: Electron-specific modules (e.g.,
app,BrowserWindow) that bridge Chromium and Node.js, enabling window management, app lifecycle control, and more.
By combining these technologies, Electron allows you to write code once and deploy it as a native app for Windows, macOS, and Linux—no need to learn platform-specific languages like C# (Windows), Swift (macOS), or Qt (Linux).
Why Choose Electron?
Electron has gained widespread adoption for several reasons:
- Cross-Platform Simplicity: Build once, run everywhere (Windows, macOS, Linux) with minimal platform-specific adjustments.
- Familiar Web Stack: Use HTML, CSS, and JavaScript—skills many developers already possess—instead of learning new languages.
- Rich Ecosystem: Access thousands of Node.js packages and web libraries (React, Vue, Angular, etc.) to accelerate development.
- Native Capabilities: Control windows, menus, tray icons, file systems, and hardware via Electron’s APIs.
- Active Community: Backed by GitHub and a large community, ensuring regular updates, bug fixes, and extensive documentation.
Note: Electron apps can be larger in size than native apps (due to bundling Chromium) and may have slightly higher resource usage. However, these trade-offs are often acceptable for the speed and simplicity of development.
Setting Up Your Development Environment
Before building your first Electron app, ensure you have the following tools installed:
- Node.js: Electron requires Node.js (v14 or later) and npm (Node Package Manager). Download it from nodejs.org.
- Code Editor: Use VS Code (ironically, an Electron app!), Sublime Text, or any editor of your choice.
Verify your setup by running these commands in your terminal:
node -v # Should output v14.x.x or higher
npm -v # Should output 6.x.x or higher
Building Your First Electron App: A Step-by-Step Guide
Let’s create a simple “Hello World” app to understand Electron’s basics. We’ll build a window that displays a message and includes a button to interact with the main process.
Project Structure
First, create a new project folder and initialize it with npm:
mkdir electron-hello-world
cd electron-hello-world
npm init -y
This creates a package.json file, which manages dependencies and app configuration.
Your project will need three key files:
electron-hello-world/
├── main.js # Main process (controls app lifecycle)
├── index.html # Renderer process (UI)
└── package.json # App metadata and scripts
The Main Process (main.js)
The main.js file is the entry point of your Electron app. It controls the main process, which manages windows, app lifecycle, and native integrations.
Add this code to main.js:
// Import Electron modules
const { app, BrowserWindow } = require('electron');
const path = require('path');
// Create a window when the app is ready
function createWindow() {
// Configure the window
const mainWindow = new BrowserWindow({
width: 800, // Window width
height: 600, // Window height
title: "My First Electron App",
webPreferences: {
nodeIntegration: false, // Disable Node.js in renderer (security best practice)
contextIsolation: true, // Isolate renderer context (security)
preload: path.join(__dirname, 'preload.js') // Preload script (see note below)
}
});
// Load the HTML file into the window
mainWindow.loadFile('index.html');
// Open DevTools (for debugging)
mainWindow.webContents.openDevTools();
}
// Electron is ready to create windows
app.whenReady().then(() => {
createWindow();
// On macOS, re-create a window if the dock icon is clicked and no windows exist
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// Quit the app when all windows are closed (except on macOS)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
Note: preload.js is a script that runs in the renderer process before the page loads, allowing limited access to Node.js APIs (if needed) while maintaining security. For now, create an empty preload.js file in your project root.
The Renderer Process (index.html)
The renderer process handles the app’s UI. Create index.html with this content:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello Electron!</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 2rem; }
button { padding: 0.5rem 1rem; font-size: 1rem; cursor: pointer; }
</style>
</head>
<body>
<h1>Hello, Electron!</h1>
<p>This is my first cross-platform desktop app.</p>
<button id="greetBtn">Click Me!</button>
<script>
// Renderer process logic
document.getElementById('greetBtn').addEventListener('click', () => {
alert('Hello from the renderer process!');
});
</script>
</body>
</html>
Configuring package.json
Update package.json to define your app’s entry point and add scripts to run it. Replace the default content with:
{
"name": "electron-hello-world",
"version": "1.0.0",
"description": "A simple Electron app",
"main": "main.js", // Entry point for the main process
"scripts": {
"start": "electron ." // Command to run the app
},
"keywords": ["electron", "desktop", "javascript"],
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"electron": "^28.0.0" // Use the latest stable Electron version
}
}
Install Electron as a dev dependency:
npm install electron --save-dev
Running the App
Start the app with:
npm start
You should see a window with your “Hello, Electron!” message and a button. Clicking the button triggers an alert—your first Electron app is working!
Core Concepts: Main vs. Renderer Processes
Electron apps have two types of processes, each with distinct roles:
Main Process
- Purpose: Manages app lifecycle (start, quit), window creation, and native integrations (menus, tray icons, file system access).
- Entry Point: Defined by the
"main"field inpackage.json(e.g.,main.js). - APIs: Access to Electron’s core modules like
app,BrowserWindow, andMenu.
Renderer Process
- Purpose: Renders the UI using HTML, CSS, and JavaScript (like a web page). Each window in your app runs its own renderer process.
- Entry Point: The HTML file loaded by
BrowserWindow(e.g.,index.html). - APIs: Limited to web APIs by default (e.g., DOM,
fetch), but can access Node.js/Electron APIs via IPC (see below).
Inter-Process Communication (IPC)
Since main and renderer processes are isolated, they communicate via IPC (Inter-Process Communication). Electron provides ipcMain (main process) and ipcRenderer (renderer process) modules for this.
Example: Send a message from renderer to main process
- Update
preload.jsto expose IPC functionality safely (required forcontextIsolation: true):
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
// Expose a safe API to the renderer process
contextBridge.exposeInMainWorld('electronAPI', {
sendMessage: (message) => ipcRenderer.send('message-from-renderer', message),
onReply: (callback) => ipcRenderer.on('reply-from-main', (event, arg) => callback(arg))
});
- Update
main.jsto listen for messages and reply:
// Add this to main.js (after creating the window)
const { ipcMain } = require('electron');
ipcMain.on('message-from-renderer', (event, message) => {
console.log('Main process received:', message);
event.reply('reply-from-main', 'Message received! Thanks from main.');
});
- Update
index.htmlto use the exposed API:
<!-- Replace the button click handler in index.html -->
<script>
document.getElementById('greetBtn').addEventListener('click', () => {
// Send a message to the main process
window.electronAPI.sendMessage('Hello from renderer!');
// Listen for a reply
window.electronAPI.onReply((reply) => {
alert(reply); // Shows "Message received! Thanks from main."
});
});
</script>
Run npm start again. Clicking the button now sends a message to the main process, which replies—demonstrating IPC!
Packaging and Distributing Your Electron App
Once your app is ready, package it into a native installer (e.g., .exe for Windows, .dmg for macOS, .deb for Linux). We’ll use electron-builder, a popular tool for packaging.
Using electron-builder
Install electron-builder as a dev dependency:
npm install electron-builder --save-dev
Update package.json to add packaging scripts:
"scripts": {
"start": "electron .",
"package": "electron-builder" // Packages for the current platform
}
Add a "build" section to package.json to configure packaging:
"build": {
"appId": "com.yourname.electronhelloworld", // Unique ID (reverse domain format)
"productName": "HelloElectron", // App name
"directories": {
"output": "dist" // Where to save packaged files
},
"win": {
"target": "nsis" // Windows installer (NSIS)
},
"mac": {
"target": "dmg" // macOS disk image
},
"linux": {
"target": "deb" // Linux Debian package
}
}
Targeting Multiple Platforms
To package for a specific platform (even if you’re on another OS), use these commands:
# Package for Windows (from macOS/Linux)
npm run package -- --win
# Package for macOS (from Windows/Linux, requires macOS dependencies)
npm run package -- --mac
# Package for Linux (from Windows/macOS)
npm run package -- --linux
Packaged apps will appear in the dist folder.
Advanced Features
Native Menus and Tray Icons
Electron lets you add native menus (top bar on macOS, window bar on Windows) and tray icons (system tray).
Example: Add a Menu
Update main.js to create a menu:
const { Menu } = require('electron');
const menuTemplate = [
{
label: 'File',
submenu: [
{ label: 'Exit', click: () => app.quit() }
]
},
{
label: 'Help',
submenu: [
{ label: 'About', click: () => alert('HelloElectron v1.0') }
]
}
];
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
Auto-Updates
Keep users on the latest version with electron-updater (built into electron-builder). Configure it by adding a "publish" field to package.json:
"build": {
"publish": {
"provider": "github", // Publish updates to GitHub Releases
"repo": "electron-hello-world", // Your GitHub repo
"owner": "your-github-username"
}
}
Debugging Tools
- Renderer Process: Use Chrome DevTools (Ctrl+Shift+I or Cmd+Opt+I in the app window).
- Main Process: Add
--inspect=5858to thestartscript:"start": "electron --inspect=5858 .", then debug in Chrome atchrome://inspect.
Best Practices for Electron Development
- Security First:
- Enable
contextIsolation: trueandsandbox: trueinBrowserWindowto isolate the renderer process. - Avoid
nodeIntegration: true(unsafe) unless absolutely necessary. Use IPC instead.
- Enable
- Optimize Performance:
- Minimize renderer processes (each window is a renderer).
- Use
webContents.on('did-finish-load')to delay heavy tasks until the page loads.
- Code Structure:
- Separate main/renderer logic into distinct files.
- Use TypeScript for type safety in larger apps.
- Testing:
- Use
spectron(Electron’s official testing framework) for end-to-end tests.
- Use
Conclusion
Electron simplifies cross-platform desktop app development by leveraging web technologies. With its powerful APIs, active community, and seamless integration of Chromium and Node.js, you can build feature-rich apps for Windows, macOS, and Linux in record time.
Start small (like our “Hello World” app), experiment with IPC and native features, and gradually scale up. The possibilities are endless—from productivity tools to media players, Electron empowers you to turn web skills into desktop solutions.