Mastering Electron IPC: From Hello World to Secure Desktop Apps

This guide walks you through the fundamentals of Electron, explaining what it is, its capabilities, how to set up a project, create windows, manage lifecycle events, implement preload scripts, and establish robust inter‑process communication (IPC) between the main and renderer processes with practical code examples.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Mastering Electron IPC: From Hello World to Secure Desktop Apps
Recently I integrated some scheduling system features into the solar editor, which is built on the interactive courseware editor Cocos ICE and customized with Cocos Creator; since Cocos Creator is based on Electron, I learned about Electron IPC communication and present a summary here.

First, let's understand what Electron is.

1. What is Electron?

Electron

is described on its official site as a framework for building cross‑platform desktop applications using JavaScript, HTML, and CSS. In simple terms, with Electron you can write a web page with front‑end technologies and package it as a desktop app. Electron also has other features:

Based on Chromium and Node.js

Compatible with macOS, Windows, and Linux, allowing you to build applications for all three platforms

2. What can Electron do?

Electron

is essentially a small Chrome‑like browser built on Chromium and Node.js. It can localize your web page (HTML file) and package it as a desktop application, while also providing many native system integration capabilities. Because it is based on Chromium, front‑end compatibility issues disappear, and the Node.js version is fixed, so you don't need to worry about version compatibility (unless you upgrade major versions). Therefore, for front‑end developers wanting to create desktop applications, Electron is an ideal choice. Notable applications built with Electron include VSCode.

3. Electron Quick Start

First, create a hello world project.

3.1. Initialize the project

Create an Electron project just like a front‑end project: make a directory and initialize it with npm.

mkdir hello-electron && cd hello-electron
npm init -y

The generated package.json looks like this:

{
  "name": "hello-electron",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {"test": "echo \"Error: no test specified\" && exit 1"},
  "keywords": [],
  "author": "",
  "license": "ISC"
}

3.2. Install dependencies

npm install --save-dev electron
During installation the electron module downloads pre‑compiled binaries from GitHub, which can be slow; you can use the Taobao mirror to speed up the download.
npm config set electron_mirror http://npm.taobao.org/mirrors/electron/
npm config set electron_custom_dir "8.1.1"

Add a start script for convenience:

{
  "scripts": {"start": "electron ."}
}

Now you can start coding.

3.3. Create HTML

Each Electron window can load a local or remote URL; create a local HTML file.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Electron <span id="electron-version"></span>
  </body>
</html>

The span will be filled dynamically with the Electron version.

3.4. Create the entry file

Like a Node.js server, Electron needs an entry file index.js that loads the HTML using two modules: app (controls the application lifecycle) and BrowserWindow (creates and manages windows).

const { app, BrowserWindow } = require('electron');
function createWindow () {
  const win = new BrowserWindow({width: 800, height: 600});
  win.loadFile('index.html');
}
app.whenReady().then(() => { createWindow(); });

The window is created after the app module emits the ready event.

npx electron .

3.5. Manage window lifecycle

Different OSes have different window behaviors. Use process.platform to handle platform‑specific logic.

3.5.1. Quit when all windows are closed (Windows & Linux)

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit();
});

3.5.2. Re‑open a window on macOS when the app is activated

app.whenReady().then(() => { createWindow(); })
app.on('activate', function () {
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

3.6. Preload scripts

Since the main process cannot directly manipulate the DOM of the renderer process, a preload script runs before the renderer loads and can access both the Node.js environment and the renderer globals.

3.6.1. Create a preload script

window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector);
    if (element) element.innerText = text;
  };
  replaceText('electron-version', process.versions.electron);
});

Pass the preload script when creating the BrowserWindow:

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');
}

4. Electron Process Model

Understanding the main and renderer processes is crucial for mastering Electron IPC. Electron inherits Chromium's multi‑process architecture, which front‑end engineers should be familiar with.

4.1. Main process

Each Electron app has a single main process (e.g., index.js) running in a Node.js environment, giving it access to require and all Node.js APIs. The main process handles three major responsibilities:

Window management : uses the BrowserWindow module to create and manage application windows.

Application lifecycle : uses the app module to control the app’s lifecycle.

Native APIs : provides modules for menus, dialogs, tray icons, etc.

4.2. Renderer process

Each BrowserWindow spawns its own renderer process, which renders the web page. The renderer runs standard web code (HTML, CSS, JavaScript) but cannot directly use require or other Node.js APIs unless explicitly enabled.

Note: Historically the renderer could access a full Node.js environment, but this is disabled by default for security reasons.

4.3. Preload script

The preload script runs before the renderer loads, has access to both Node.js and the renderer globals, and can safely expose selected APIs to the renderer via contextBridge.

5. Electron IPC Communication

Electron provides two modules for inter‑process communication: ipcMain (in the main process) and ipcRenderer (in the renderer).

5.1. Renderer → Main and reply

5.1.1. Simple script listener

In the renderer, import ipcRenderer and send a message:

const { ipcRenderer } = require('electron');
ipcRenderer.on('main-message-reply', (event, arg) => { console.log(arg); });
ipcRenderer.send('message-from-renderer', 'Message from renderer');

In the main process, listen with ipcMain and reply:

const { ipcMain } = require('electron');
ipcMain.on('message-from-renderer', (event, arg) => {
  console.log(arg);
  event.reply('main-message-reply', 'Reply from main');
});

5.1.2. Expose API via preload

// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('myAPI', {
  getMessage(args) {
    ipcRenderer.send('message-from-preload', args);
    console.log('Renderer called:', args);
  }
});

Renderer can then call window.myAPI.getMessage('postMessage'), and the main process handles it with ipcMain.on('message-from-preload', ...).

5.2. Main → Renderer

To push a message from the main process, use the window’s webContents:

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: true, contextIsolation: false }
  });
  const contents = mainWindow.webContents;
  mainWindow.loadFile('index.html');
  contents.on('did-finish-load', () => {
    contents.send('main-message-reply', 'I see you loaded, here is a message');
  });
}

In the renderer, listen for the message:

const { ipcRenderer } = require('electron');
ipcRenderer.on('main-message-reply', (event, arg) => {
  console.log('Main process pushed:', arg);
});

All shown communication methods are asynchronous; synchronous IPC exists but can block execution and is generally discouraged.

6. Conclusion

This article covered the basics of Electron, its process model, and practical IPC patterns. While native IPC can feel cumbersome, frameworks like VSCode provide higher‑level abstractions that are worth exploring in future deep‑dives.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ElectronNode.jsIPCdesktop appBrowserWindowPreload Script
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.