Electron API Overview
Classification and Role of Electron APIs
Electron APIs are divided into two categories:
- Main Process APIs:
- Control application lifecycle and system interactions.
- Examples:
app,BrowserWindow,dialog,Menu.
- Renderer Process APIs:
- Handle UI rendering and communication with the main process.
- Examples:
ipcRenderer,webContents.
Differences Between Main and Renderer Process APIs
- Main Process:
- Runs Node.js with full access to Node APIs.
- Single instance, manages all windows.
- Renderer Process:
- Runs Chromium, resembling a browser environment.
- Multiple instances, one per window.
Development Environment Setup
Initialize Project
mkdir ElectronAPIDemo
cd ElectronAPIDemo
npm init -y
npm install electron --save-devConfigure package.json
{
"name": "electron-api-demo",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^28.2.2"
}
}Main Process APIs
Creating and Controlling Browser Windows (BrowserWindow)
BrowserWindow is a core main process API for creating and managing windows.
Example Code: Basic Window
// main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
x: 100, // Window position
y: 100,
title: 'My Electron App',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
win.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});Step-by-Step Analysis
new BrowserWindow: Creates a window instance.width/height: Sets window dimensions.x/y: Sets window position.webPreferences: Configures renderer process options.
loadFile: Loads an HTML file.app.whenReady: Triggers when the app is ready.
Controlling Windows
function createWindow() {
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
win.maximize(); // Maximize
win.on('closed', () => console.log('Window closed'));
setTimeout(() => win.setTitle('Updated Title'), 2000); // Update title after 2 seconds
}Analysis
maximize: Maximizes the window.setTitle: Dynamically sets the title.on('closed'): Listens for window close events.
System Dialogs (dialog)
The dialog module provides file selection and message prompt functionality.
Example Code: File Dialog
const { app, BrowserWindow, dialog, ipcMain } = require('electron');
function createWindow() {
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
});
ipcMain.handle('open-file-dialog', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text Files', extensions: ['txt'] }],
});
return canceled ? null : filePaths[0];
});
ipcMain.handle('show-message', async () => {
const result = await dialog.showMessageBox({
type: 'info',
title: 'Hello',
message: 'This is a test message',
buttons: ['OK', 'Cancel'],
});
return result.response === 0 ? 'OK' : 'Cancel';
});Analysis
showOpenDialog: Opens a file selection dialog.properties: Specifies dialog type.filters: Restricts file types.
showMessageBox: Displays a message box.buttons: Custom buttons.response: Returns user selection.
Menus (Menu)
The Menu module customizes application menus.
Example Code
const { app, BrowserWindow, Menu } = require('electron');
function createWindow() {
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
const menu = Menu.buildFromTemplate([
{
label: 'File',
submenu: [
{ label: 'New', click: () => console.log('New clicked') },
{ label: 'Quit', accelerator: 'CmdOrCtrl+Q', click: () => app.quit() },
],
},
{ label: 'Edit', submenu: [{ label: 'Copy', role: 'copy' }] },
]);
Menu.setApplicationMenu(menu);
}
app.whenReady().then(createWindow);Analysis
buildFromTemplate: Creates menu structure.accelerator: Defines shortcuts.role: Uses built-in roles (e.g.,copy).
Renderer Process APIs
Communicating with the Main Process (ipcMain and ipcRenderer)
ipcMain and ipcRenderer enable bidirectional communication between main and renderer processes.
Example Code
main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
ipcMain.handle('send-data', async (event, data) => {
console.log('Received:', data);
return 'Data received by main!';
});preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
sendData: (data) => ipcRenderer.invoke('send-data', data),
});index.html
<!DOCTYPE html>
<html>
<head>
<title>IPC Communication</title>
</head>
<body>
<h1>IPC Communication</h1>
<button onclick="sendData()">Send Data</button>
<script>
function sendData() {
window.electronAPI.sendData('Hello from renderer!').then(response => {
alert(response);
});
}
</script>
</body>
</html>Analysis
ipcMain.handle: Main process handles messages.ipcRenderer.invoke: Renderer process sends and awaits reply.contextBridge: Safely exposes APIs.
Window Operations in the Renderer Process
The renderer process can indirectly control windows via webContents or IPC.
Example Code
main.js
ipcMain.on('minimize-window', (event) => {
BrowserWindow.fromWebContents(event.sender).minimize();
});index.html
<button onclick="minimizeWindow()">Minimize</button>
<script>
function minimizeWindow() {
require('electron').ipcRenderer.send('minimize-window');
}
</script>Analysis
fromWebContents: Retrieves window instance from event source.minimize: Minimizes the window.
File System Operations
Reading Files and Directories (fs Module)
Electron integrates Node.js’s fs module.
Example Code
// main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const fs = require('fs').promises;
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: { preload: path.join(__dirname, 'preload.js') },
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
ipcMain.handle('read-file', async (event, filePath) => {
try {
const content = await fs.readFile(filePath, 'utf8');
return content;
} catch (error) {
return error.message;
}
});
ipcMain.handle('read-dir', async (event, dirPath) => {
try {
const files = await fs.readdir(dirPath);
return files;
} catch (error) {
return error.message;
}
});preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('fsAPI', {
readFile: (path) => ipcRenderer.invoke('read-file', path),
readDir: (path) => ipcRenderer.invoke('read-dir', path),
});index.html
<!DOCTYPE html>
<html>
<head>
<title>File System Demo</title>
</head>
<body>
<h1>File System Demo</h1>
<button onclick="readFile()">Read File</button>
<button onclick="readDir()">Read Directory</button>
<script>
function readFile() {
window.fsAPI.readFile('example.txt').then(content => alert(content));
}
function readDir() {
window.fsAPI.readDir('.').then(files => alert(files.join('\n')));
}
</script>
</body>
</html>Analysis
fs.readFile: Reads file content.fs.readdir: Lists directory contents.- Asynchronous: Uses
promisesAPI.
Writing Files and Directories
Example Code
ipcMain.handle('write-file', async (event, filePath, content) => {
try {
await fs.writeFile(filePath, content, 'utf8');
return 'Write successful';
} catch (error) {
return error.message;
}
});
ipcMain.handle('create-dir', async (event, dirPath) => {
try {
await fs.mkdir(dirPath, { recursive: true });
return 'Directory created';
} catch (error) {
return error.message;
}
});preload.js
contextBridge.exposeInMainWorld('fsAPI', {
writeFile: (path, content) => ipcRenderer.invoke('write-file', path, content),
createDir: (path) => ipcRenderer.invoke('create-dir', path),
});index.html
<button onclick="writeFile()">Write File</button>
<button onclick="createDir()">Create Directory</button>
<script>
function writeFile() {
window.fsAPI.writeFile('test.txt', 'Hello, Electron!').then(result => alert(result));
}
function createDir() {
window.fsAPI.createDir('newFolder').then(result => alert(result));
}
</script>Analysis
fs.writeFile: Writes to a file.fs.mkdir: Creates a directory,recursivesupports nested creation.
Practical File System Example
Example: File List Management
ipcMain.handle('list-files', async (event, dirPath) => {
const files = await fs.readdir(dirPath, { withFileTypes: true });
return files.map(file => ({
name: file.name,
isDir: file.isDirectory(),
}));
});index.html
<button onclick="listFiles()">List Files</button>
<script>
function listFiles() {
window.fsAPI.listFiles('.').then(files => {
const list = files.map(f => `${f.name} (${f.isDir ? 'Dir' : 'File'})`).join('\n');
alert(list);
});
}
</script>Analysis
withFileTypes: Distinguishes files and directories.
Network Requests
Making Requests with http/https Modules
Electron uses Node.js’s http and https modules.
Example Code
const { app, BrowserWindow, ipcMain } = require('electron');
const https = require('https');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: { preload: './preload.js' },
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
ipcMain.handle('fetch-data', async () => {
return new Promise((resolve, reject) => {
https.get('https://api.github.com', (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => resolve(JSON.parse(data)));
}).on('error', (err) => reject(err.message));
});
});preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('networkAPI', {
fetchData: () => ipcRenderer.invoke('fetch-data'),
});index.html
<!DOCTYPE html>
<html>
<head>
<title>Network Demo</title>
</head>
<body>
<h1>Network Demo</h1>
<button onclick="fetchData()">Fetch Data</button>
<script>
function fetchData() {
window.networkAPI.fetchData().then(data => alert(JSON.stringify(data, null, 2)));
}
</script>
</body>
</html>Analysis
https.get: Initiates a GET request.- Stream Processing: Receives data in chunks.
Handling Responses and Errors
Example Extension
ipcMain.handle('fetch-data', async () => {
return new Promise((resolve, reject) => {
const req = https.get('https://api.github.com', {
headers: { 'User-Agent': 'ElectronApp' },
}, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => {
if (res.statusCode === 200) resolve(JSON.parse(data));
else reject(new Error(`Status: ${res.statusCode}`));
});
});
req.on('error', (err) => reject(err.message));
req.end();
});
});Analysis
headers: Custom request headers.- Error Handling: Checks status code and network errors.
Advanced Network Request Usage
POST Request
ipcMain.handle('post-data', async (event, payload) => {
return new Promise((resolve, reject) => {
const data = JSON.stringify(payload);
const options = {
hostname: 'api.example.com',
path: '/submit',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data),
},
};
const req = https.request(options, (res) => {
let response = '';
res.on('data', (chunk) => response += chunk);
res.on('end', () => resolve(response));
});
req.on('error', (err) => reject(err.message));
req.write(data);
req.end();
});
});Analysis
https.request: Supports complex requests.write: Sends request body.
Comprehensive Case Study: File Manager Application
Requirements Analysis
Implement a file manager:
- Display a directory file list.
- Support saving and loading files.
- Fetch data via network requests.
Implementation Code Breakdown
main.js
const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron');
const fs = require('fs').promises;
const path = require('path');
const https = require('https');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
win.loadFile('index.html');
const menu = Menu.buildFromTemplate([
{
label: 'File',
submenu: [
{ label: 'Open', click: () => win.webContents.send('open-file') },
{ label: 'Save', click: () => win.webContents.send('save-file') },
{ type: 'separator' },
{ label: 'Quit', click: () => app.quit() },
],
},
]);
Menu.setApplicationMenu(menu);
}
app.whenReady().then(createWindow);
ipcMain.handle('list-files', async (event, dirPath) => {
const files = await fs.readdir(dirPath || __dirname, { withFileTypes: true });
return files.map(file => ({ name: file.name, isDir: file.isDirectory() }));
});
ipcMain.handle('save-file', async (event, content) => {
const { filePath } = await dialog.showSaveDialog({
defaultPath: 'note.txt',
filters: [{ name: 'Text Files', extensions: ['txt'] }],
});
if (filePath) {
await fs.writeFile(filePath, content);
return filePath;
}
return null;
});
ipcMain.handle('load-file', async () => {
const { filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text Files', extensions: ['txt'] }],
});
if (filePaths && filePaths.length > 0) {
return await fs.readFile(filePaths[0], 'utf8');
}
return null;
});
ipcMain.handle('fetch-data', async () => {
return new Promise((resolve, reject) => {
https.get('https://api.github.com', {
headers: { 'User-Agent': 'ElectronFileManager' },
}, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => resolve(JSON.parse(data)));
}).on('error', (err) => reject(err.message));
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
listFiles: (dirPath) => ipcRenderer.invoke('list-files', dirPath),
saveFile: (content) => ipcRenderer.invoke('save-file', content),
loadFile: () => ipcRenderer.invoke('load-file'),
fetchData: () => ipcRenderer.invoke('fetch-data'),
onOpenFile: (callback) => ipcRenderer.on('open-file', callback),
onSaveFile: (callback) => ipcRenderer.on('save-file', callback),
});index.html
<!DOCTYPE html>
<html>
<head>
<title>File Manager</title>
</head>
<body>
<h1>File Manager</h1>
<input id="dirPath" placeholder="Enter directory path" />
<button onclick="listFiles()">List Files</button>
<ul id="fileList"></ul>
<textarea id="content" rows="10" cols="50"></textarea><br>
<button onclick="saveFile()">Save File</button>
<button onclick="loadFile()">Load File</button>
<button onclick="fetchData()">Fetch Network Data</button>
<script>
function listFiles() {
const dirPath = document.getElementById('dirPath').value;
window.electronAPI.listFiles(dirPath).then(files => {
const list = document.getElementById('fileList');
list.innerHTML = files.map(f => `<li>${f.name} (${f.isDir ? 'Dir' : 'File'})</li>`).join('');
});
}
function saveFile() {
const content = document.getElementById('content').value;
window.electronAPI.saveFile(content).then(filePath => {
if (filePath) alert('Saved to: ' + filePath);
});
}
function loadFile() {
window.electronAPI.loadFile().then(content => {
if (content) document.getElementById('content').value = content;
});
}
function fetchData() {
window.electronAPI.fetchData().then(data => alert(JSON.stringify(data, null, 2)));
}
window.electronAPI.onOpenFile(() => loadFile());
window.electronAPI.onSaveFile(() => saveFile());
</script>
</body>
</html>package.json
{
"name": "file-manager",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^28.2.2"
}
}Run
npm startAnalysis
- Main Process: Manages windows, file operations, and network requests.
- Renderer Process: Displays file list and content.
- IPC: Enables bidirectional communication.
- Features: File list display, save/load, network data fetching.
Advanced Techniques and Considerations
Performance Optimization Tips
- Reduce IPC Calls: Batch data transfers.
- Asynchronous File Operations: Avoid blocking the main thread.
- Cache Network Responses: Store frequently requested data locally.
Debugging and Error Handling
- Developer Tools: Open renderer process debugger with Ctrl+Shift+I.
- Main Process Debugging:
electron . --inspect- Error Capture:
process.on('uncaughtException', (error) => {
console.error('Uncaught:', error);
});



