YouTip LogoYouTip

Electron Architecture

Electron Architecture | Rookie Tutorial

Think of Electron as:

A multi-process application container with a built-in browser engine (Chromium) and a Node.js runtime,
which works together through inter-process communication (IPC) to achieve "web + system capabilities" desktop applications.

Its core components are:

Role Description Analogy
Main Process The brain of Electron, controls application lifecycle, creates windows, calls system APIs OS Manager
Renderer Process The web environment (HTML, CSS, JS) running in each window Browser Tab
Preload Script Runs before rendering, can bridge main process APIs to the web page Security "Middleware"
IPC Communication channel between main process and renderer process Telephone Line
BrowserWindow Object The "window container" created by the main process, loads web pages inside Browser Window
App Module Controls application lifecycle (launch, quit) Master Control

Image 1

Main Process

The main process is the "brain" of an Electron application. Each Electron application has one and only one main process. It is responsible for:

  • Creating and managing application windows (renderer processes)
  • Handling application lifecycle (launch, quit, foreground/background switching)
  • Interacting with native OS APIs
  • Managing system-level components like menus and dialogs

Example

// main.js - Main process example

const { app, BrowserWindow } = require('electron');

function createWindow() {
  // Create browser window
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  });

  // Load the application's index.html
  mainWindow.loadFile('index.html');
}

// Called when Electron finishes initialization
app.whenReady().then(createWindow);

Renderer Process

The renderer process is responsible for displaying the user interface. Each Electron window is an independent renderer process:

  • Runs in the Chromium browser environment
  • Uses HTML, CSS, and JavaScript to build the interface
  • Each window is an independent process, unaffected by others
  • Communicates with the main process through IPC

Example

<!-- index.html - Renderer process example -->

<!DOCTYPE html>
<html>
<head>
  <title>My Electron App</title>
</head>
<body>
  <h1>Hello Electron!</h1>
  <button id="btn">Click Me</button>

  <script>
    // JavaScript in renderer process
    document.getElementById('btn').addEventListener('click', () => {
      alert('Button clicked!');
    });
  </script>
</body>
</html>

Preload Scripts

Preload scripts are the "bridge" connecting the main process and renderer process:

  • Runs before the renderer process loads the web page
  • Has access to Node.js APIs and DOM
  • Securely exposes APIs to the renderer process through contextBridge

Example

// preload.js - Preload script example

const { contextBridge, ipcRenderer } = require('electron');

// Expose secure API to renderer process
contextBridge.exposeInMainWorld('electronAPI', {
  showDialog: (message) => ipcRenderer.invoke('show-dialog', message)
});

Inter-Process Communication (IPC)

IPC Communication Patterns

IPC (Inter-Process Communication) is the core of Electron's communication.

Since the main process and renderer process are independent, data must be passed through IPC.

Image 2

Communication Directions

Type Main Process Listens Renderer Process Sends
Renderer β†’ Main ipcMain.on(channel, handler) ipcRenderer.send(channel, data)
Main β†’ Renderer event.sender.send(channel, data) ipcRenderer.on(channel, callback)

Basic Communication Example

Main Process Code:

Example

const { ipcMain, dialog } = require('electron');

// Listen for messages from renderer process
ipcMain.handle('show-dialog', async (event, message) => {
  const result = await dialog.showMessageBox({
    type: 'info',
    message: message,
    buttons: ['OK', 'Cancel']
  });
  return result;
});

Renderer Process Code:

Example

// Communicate through API exposed by preload script
document.getElementById('btn').addEventListener('click', async () => {
  const result = await window.electronAPI.showDialog('Hello, Electron!');
  console.log('User clicked:', result.response);
});

Architecture Advantages and Characteristics

Advantages Comparison

Feature Electron Traditional Desktop Development
Development Technology Web technologies (HTML/CSS/JS) Native languages (C++/C#/Java)
Cross-platform Support Develop once, run on multiple platforms Need separate development for each platform
Development Efficiency High, leverages existing web ecosystem Lower, requires learning platform-specific technologies
Performance Relatively lower, more resource consumption High, native performance
Package Size Larger (includes Chromium) Smaller

Core Characteristics

  1. Cross-platform Consistency: Applications look and behave the same across all operating systems
  2. Web Ecosystem Utilization: Can directly use npm packages and web frameworks
  3. Rapid Prototyping: Based on familiar web technologies, shorter development cycles
  4. Rich APIs: Provides a complete set of APIs for accessing native system functionality

Practical Application Example

Let's create a simple file manager application to demonstrate Electron architecture:

Main Process (main.js):

Example

const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const fs = require('fs').promises;
const path = require('path');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000,
    height: 700,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true
    }
  });

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

// Handle file reading
ipcMain.handle('read-file', async (event, filePath) => {
  try {
    const content = await fs.readFile(filePath, 'utf-8');
    return { success: true, content };
  } catch (error) {
    return { success: false, error: error.message };
  }
});

// Handle file selection
ipcMain.handle('select-file', async () => {
  const result = await dialog.showOpenDialog(mainWindow, {
    properties: ['openFile'],
    filters: [
      { name: 'Text Files', extensions: ['txt', 'md', 'js', 'html', 'css'] }
    ]
  });
  return result;
});

app.whenReady().then(createWindow);

Preload Script (preload.js):

Example

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('fileAPI', {
  readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
  selectFile: () => ipcRenderer.invoke('select-file')
});

Renderer Process (index.html):

Example

<!DOCTYPE html>
<html>
<head>
  <title>Simple File Manager</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    .container { max-width: 800px; margin: 0 auto; }
    button { padding: 10px 15px; margin: 5px; cursor: pointer; }
    #content { border: 1px solid #ccc; padding: 15px; margin-top: 10px; 
      min-height: 300px; white-space: pre-wrap; }
  </style>
</head>
<body>
  <div class="container">
    <h1>Simple File Manager</h1>
    <button id="selectBtn">Select File</button>
    <button id="clearBtn">Clear Content</button>
    
    <div>
      <h3>File Content:</h3>
      <div id="content">Please select a file...</div>
    </div>
  </div>

  <script>
    const selectBtn = document.getElementById('selectBtn');
    const clearBtn = document.getElementById('clearBtn');
    const contentDiv = document.getElementById('content');

    selectBtn.addEventListener('click', async () => {
      const result = await window.fileAPI.selectFile();
      if (!result.canceled && result.filePaths.length > 0) {
        const filePath = result.filePaths;
        const fileResult = await window.fileAPI.readFile(filePath);
        if (fileResult.success) {
          contentDiv.textContent = `File path: ${filePath}nn${fileResult.content}`;
        } else {
          contentDiv.textContent = `Failed to read file: ${fileResult.error}`;
        }
      }
    });

    clearBtn.addEventListener('click', () => {
      contentDiv.textContent = 'Please select a file...';
    });
  </script>
</body>
</html>

Other Important Modules (Available in Main Process)

Module Name Function
app Controls application lifecycle (launch/quit)
Menu / MenuItem Creates application menu bar
Tray System tray icon
Notification System notifications
dialog Open file/save dialogs
shell Opens files or URLs with default system programs
nativeImage Manipulates images (icons)
clipboard Manipulates clipboard content
powerMonitor Listens to system power events
screen Gets screen information (multi-monitor support)

Security Architecture and Sandbox Isolation

Since renderer processes can run web pages, Electron implements the following security designs to prevent malicious code from attacking the system:

Strategy Description
Disable NodeIntegration Default false, prevents web pages from directly calling system APIs.
Enable ContextIsolation Keeps page scripts and Preload API running in isolated contexts.
Preload + contextBridge Explicitly controls the secure APIs exposed to the frontend.
Content Security Policy (CSP) Restricts script sources, prevents XSS.
Sandbox Mode Can enable sandbox to completely isolate renderer processes.
Validate Remote URLs Untrusted remote content must be whitelist-validated.

Runtime Flow (From Launch to Render)

  1. Launch application (electron .) ↓
  2. Execute main.js (main process starts) ↓
  3. app.whenReady() triggers, creates BrowserWindow ↓
  4. BrowserWindow starts new renderer process (Chromium instance) ↓
  5. preload.js executes first (in isolated context) ↓
  6. index.html loads and displays (frontend framework runs) ↓
  7. Renderer process calls main process logic through IPC ↓
  8. Main process handles request, returns result ↓
  9. User closes window β†’ main process listens β†’ app.quit()

Master-Slave Multi-Window Model (Multi-Process Parallelism)

  • An Electron application always has only one main process;
  • But can have multiple renderer processes (each window is independent);
  • Communication between them must go through the main process or use ipcMain.handle for asynchronous bridging.

Illustration:

Main Process (Main)
β”œβ”€β”€ Window 1 β†’ Renderer Process A (index.html)
β”œβ”€β”€ Window 2 β†’ Renderer Process B (settings.html)
└── Window 3 β†’ Renderer Process C (dashboard.html)

Electron Internal Core Components (Underlying Dependencies)

Layer Technology
UI Rendering Layer Chromium (Blink + V8)
System Interface Layer Node.js (libuv + C++ bindings)
Process Management Layer Electron Kernel (C++ + JavaScript)
Application Logic Layer Your JS / TS code (Main + Renderer)

Your JS code runs in the Node environment packaged by Electron, while the rendering interface runs in the Chromium WebView.

← Electron StartElectron Tutorial β†’