Skip to content
Electron Desktop Apps — Complete API Reference & Guide

Electron Desktop Apps — Complete API Reference & Guide

DodaTech Updated Jun 6, 2026 6 min read

Electron is a framework for building cross-platform desktop applications using web technologies — HTML, CSS, and JavaScript — running on the Chromium engine and Node.js runtime.

What You’ll Learn

By the end of this tutorial, you’ll understand Electron’s two-process architecture, set up IPC communication between main and renderer processes, create BrowserWindows, use native dialogs and menus, and package your app for Windows, macOS, and Linux.

Why Electron Matters

Electron lets web developers build native desktop apps with their existing skills. VS Code, Slack, Discord, Figma, and thousands of other applications run on Electron. It provides native OS integration (system tray, notifications, file dialogs) while allowing you to use standard web technologies. DodaZIP, DodaTech’s file compression tool, uses Electron for its cross-platform desktop interface.

Electron Reference

Prerequisites: Solid JavaScript and Node.js knowledge. Understanding of HTML and basic operating system concepts.

Main Process

The main process creates browser windows and handles system-level interactions:

const { app, BrowserWindow, ipcMain, dialog } = require("electron");

let mainWindow;

app.whenReady().then(() => {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      nodeIntegration: false,        // security: no Node.js in renderer
      contextIsolation: true,        // security: separate contexts
      preload: path.join(__dirname, "preload.js")
    }
  });

  mainWindow.loadFile("index.html");
});

Line-by-line:

  • app.whenReady() — wait for Electron to finish initialization before creating windows
  • new BrowserWindow({...}) — creates a new native window
  • nodeIntegration: false — renderer can’t access Node.js APIs (security best practice)
  • contextIsolation: true — renderer code runs in an isolated context (security best practice)
  • preload: path.join(__dirname, "preload.js") — a bridge script that runs before the renderer, used to expose specific APIs safely

The Two-Process Architecture:

    flowchart LR
  A[Main Process<br/>Node.js] -->|ipcMain| B[Preload Script<br/>Bridge]
  B -->|contextBridge| C[Renderer Process<br/>Browser Window]
  A --> D[Native APIs<br/>Menu, Dialog, Tray]
  C --> E[Web UI<br/>HTML, CSS, JS]
  

IPC Communication

Electron keeps main and renderer processes separate for security. Communication happens through IPC (Inter-Process Communication):

// preload.js — expose safe APIs to renderer
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electronAPI", {
  getData: () => ipcRenderer.invoke("get-data"),
  saveFile: (data) => ipcRenderer.invoke("save-file", data)
});
// main.js — handle requests from renderer
ipcMain.handle("get-data", async () => {
  return await db.findMany();
});

ipcMain.handle("save-file", async (event, data) => {
  const result = await dialog.showSaveDialog(mainWindow);
  if (!result.canceled) {
    fs.writeFileSync(result.filePath, JSON.stringify(data));
  }
});

Line-by-line:

  • contextBridge.exposeInMainWorld("electronAPI", {...}) — safely exposes a window.electronAPI object to the renderer. The renderer calls window.electronAPI.getData() which triggers ipcRenderer.invoke("get-data")
  • ipcRenderer.invoke("get-data") — sends a message to the main process and returns a Promise
  • ipcMain.handle("get-data", async () => {...}) — main process listens for “get-data” requests and responds
  • dialog.showSaveDialog(mainWindow) — opens the native OS save-file dialog

Why this architecture? In early Electron versions, the renderer had full Node.js access — but this was a security nightmare (any XSS vulnerability in the UI could execute system commands). Modern Electron uses context isolation and preload scripts to expose only specific, safe APIs.

Key APIs

ModulePurpose
appApplication lifecycle (ready, quit, activate)
BrowserWindowCreate and manage native windows
ipcMain / ipcRendererProcess communication
dialogNative file dialogs (open, save, message)
MenuNative application menus and context menus
TraySystem tray icons with context menus
NotificationDesktop notifications
shellOpen files/URLs with OS defaults
clipboardSystem clipboard (read/write)
nativeImageCreate tray/app icons from images
screenDisplay information (resolution, scaling)

Native Menus

const { Menu } = require("electron");

const template = [
  { label: "File",
    submenu: [
      { label: "Open", accelerator: "CmdOrCtrl+O", click: () => openFile() },
      { type: "separator" },
      { label: "Quit", accelerator: "CmdOrCtrl+Q", role: "quit" }
    ]
  },
  { label: "Edit",
    submenu: [
      { role: "undo" },
      { role: "redo" }
    ]
  }
];

const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

Packaging

npm install --save-dev electron-builder

# Build for all platforms
npx electron-builder --win --mac --linux

# Platform-specific
npx electron-builder --win        # Windows installer
npx electron-builder --mac        # macOS .dmg
npx electron-builder --linux      # Linux AppImage / deb

What packaging does:

  1. Bundles your app code with the Electron runtime
  2. Creates platform-specific installers (.exe, .dmg, .AppImage)
  3. Handles code signing for macOS and Windows
  4. Manages auto-update infrastructure

Common Mistakes

1. Enabling nodeIntegration in production

Setting nodeIntegration: true exposes Node.js APIs to the renderer. If your app has any XSS vulnerability, attackers get full system access. Always set it to false.

2. Forgetting contextIsolation

Without contextIsolation: true, the preload script and renderer share the same JavaScript context — defeating the security benefits of the preload pattern.

3. Using sync IPC in performance-critical paths

ipcRenderer.sendSync() blocks the renderer until the main process responds. Use async ipcRenderer.invoke() instead.

4. Not handling app quit properly

On macOS, apps should stay open when all windows close (common behavior). Handle the window-all-closed event correctly per platform.

5. Building for one platform and expecting it to work everywhere

Each OS has different path conventions, file dialogs, and system APIs. Always test on all target platforms.

Practice Questions

1. Why is nodeIntegration: false important?

Answer: It prevents the renderer process from accessing Node.js APIs, protecting against XSS attacks that could execute system commands.

2. What is the role of the preload script?

Answer: The preload script runs before the renderer and uses contextBridge to safely expose specific main-process APIs to the renderer.

3. How do main and renderer processes communicate?

Answer: Through IPC — ipcRenderer.invoke() in the renderer sends a request, ipcMain.handle() in the main process handles it and returns a response.

4. What does electron-builder do?

Answer: It packages your Electron app into platform-specific installers (.exe, .dmg, .AppImage) with the Electron runtime bundled.

Challenge

Build a simple text editor with Electron: main process manages file I/O via IPC, renderer provides the editing UI, preload exposes safe APIs. Package it for your platform and test the native save/open dialogs.

FAQ

What is Electron?
Electron is a framework for building cross-platform desktop apps with web technologies (HTML, CSS, JavaScript) by combining Chromium and Node.js in a single runtime.
What is the difference between main and renderer process?
The main process runs Node.js and manages windows and system APIs. The renderer process runs the web UI in a Chromium browser context.
How do I secure my Electron app?
Disable nodeIntegration, enable contextIsolation, use preload scripts with contextBridge, validate all IPC input, and keep Electron updated.
Can Electron apps work offline?
Yes. Electron bundles its own Chromium engine, so the UI works entirely offline. Network-dependent features are your app’s responsibility.
What is the app size of an Electron app?
Expect 150-250MB for a minimal app (Chrome + Node.js bundled). This is the main criticism of Electron.
How does Electron compare to Tauri or NW.js?
Tauri is smaller (uses OS webview) but has less API surface. NW.js is similar to Electron but older. Electron has the largest ecosystem.

Try It Yourself

# 1. Create a minimal Electron app
mkdir my-electron-app && cd my-electron-app
npm init -y

# 2. Install Electron
npm install --save-dev electron

# 3. Create main.js
cat > main.js << 'EOF'
const { app, BrowserWindow } = require("electron");

app.whenReady().then(() => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true
    }
  });
  win.loadFile("index.html");
});
EOF

# 4. Create index.html
cat > index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>My Electron App</title></head>
<body>
  <h1>Hello from Electron!</h1>
  <p>This is a desktop app built with web technologies.</p>
</body>
</html>
EOF

# 5. Add start script to package.json
# (manually add: "start": "electron .")

# 6. Run your app
npx electron .

What’s Next

Explore more development tools:

TopicDescription
https://tutorials.dodatech.com/tools/rxjs/Reactive programming with Observables
https://tutorials.dodatech.com/tools/lodash/JavaScript utility library

Related topics to explore:

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro