Skip to content

Electron - Hello World

Let’s create your first modern Electron desktop application. This ‘Hello World’ example will demonstrate the basic structure, including the main process, a renderer process, and a preload script for secure communication, which is a key aspect of modern Electron development.

First, ensure you have a package.json file. If not, run npm init -y. Then install Electron: npm install --save-dev electron.

Create a file named main.js. This is the entry point of your Electron application and runs in the Main Process:

const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // Protect against prototype pollution
nodeIntegration: false // Enforce separation of concerns
}
});
// Load the index.html of the app.
mainWindow.loadFile('index.html');
// Open the DevTools (optional)
// mainWindow.webContents.openDevTools();
}
// This method will be called when Electron has finished initialization
// and is ready to create browser windows.
app.whenReady().then(() => {
createWindow();
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// Quit when all windows are closed, except on macOS.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit();
});

Next, create preload.js. This script runs in a privileged environment before the renderer’s web page is loaded. It has access to Node.js APIs and can securely expose specific functionalities to the renderer process via the contextBridge.

const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('electronVersions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron
// We can also expose variables, not just functions
});

Now, create your index.html file. This is the web page that will be displayed in your application’s window (Renderer Process):

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Hello World!</h1>
<p>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
</p>
<script src="renderer.js"></script>
</body>
</html>

Create a simple styles.css (optional):

body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #f4f4f4;
color: #333;
}
h1 {
color: #0056b3;
}
span {
font-weight: bold;
color: #28a745;
}

Finally, create renderer.js. This script runs in the renderer process and interacts with the DOM. It will use the APIs exposed by preload.js:

renderer.js
document.addEventListener('DOMContentLoaded', () => {
const nodeVersionElement = document.getElementById('node-version');
const chromeVersionElement = document.getElementById('chrome-version');
const electronVersionElement = document.getElementById('electron-version');
if (window.electronVersions) {
nodeVersionElement.textContent = window.electronVersions.node();
chromeVersionElement.textContent = window.electronVersions.chrome();
electronVersionElement.textContent = window.electronVersions.electron();
} else {
console.error('electronVersions API not found. Check preload script.');
}
});

To run your app, first add a start script to your package.json:

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

Then, run the app from your terminal:

npm start

A window will appear displaying ‘Hello World!’ and the versions of Node.js, Chromium, and Electron being used. This text is dynamically inserted by renderer.js using information securely provided by preload.js.

  1. main.js (Main Process): Manages the application lifecycle and creates browser windows (BrowserWindow). It configures webPreferences to use a preload.js script, enable contextIsolation, and disable nodeIntegration for security.

  2. preload.js: Acts as a bridge between the Node.js environment (accessible here) and the renderer’s isolated JavaScript context. It uses contextBridge.exposeInMainWorld to selectively expose specific Node.js functionalities (like process.versions) to the renderer under a chosen API namespace (e.g., window.electronVersions).

  3. index.html & renderer.js (Renderer Process): index.html defines the UI structure. renderer.js runs in this sandboxed environment. It cannot directly access Node.js APIs. Instead, it uses the functions exposed by preload.js (e.g., window.electronVersions.node()) to get the required information and update the DOM.

This structure enhances security by limiting the renderer process’s access to powerful Node.js APIs, reducing the attack surface if your application loads remote or untrusted content.