Anatomy of an Electron 4 application

Overview of main concepts in Electron 4 applications.

main process

Concerns

  • Entry point for Electron applications
  • Creates and manages BrowserWindow instances
  • Registers global shortcuts
  • Creates native menus
  • Shows native GUI
  • Responds to auto-update events

renderer process

Concerns

  • Takes care of showing your HTML & JS in the Chromium browser
  • Runs UI in webContents instances
  • Access information about audio and video devices using desktopCapturer
  • Can access main process modules via remote module

Characteristics

  • process.type is "renderer"

Example

1
2
3
4
5
6
7
const {desktopCapturer, ipcRenderer, webFrame} = require('electron');
const {app} = require('electron').remote

webFrame.setZoomFactor(1.0);
webFrame.setVisualZoomLevelLimits(1, 1);

console.log('App configuration directory', app.getPath('userData'));

Inter-process communication (IPC)

Using inter-process communication a renderer process can exchange messages with a main process:

renderer.js

1
2
3
4
5
6
7
import {ipcRenderer} from 'electron';

const updateBtn = document.getElementById('updateBtn')

updateBtn.addEventListener('click', () => {
ipcRenderer.send('my-app-event', document.getElementById('notifyVal').value);
});

main.ts

1
2
3
4
5
6
7
import {BrowserWindow, ipcMain, IpcMessageEvent} from 'electron';

const main = new BrowserWindow();

ipcMain.on('my-app-event', (event: IpcMessageEvent, price: number) => {
main.webContents.send('target-price', price);
});

Note: ipcRenderer does not send messages to itself, it sends them to ipcMain. If you want to access the messages within a renderer process, you need to check ipcMain using electron.remote:

renderer.js

1
2
3
4
5
6
7
import {remote} from 'electron';

// ...

remote.ipcMain.on('my-app-event', (event, price) => {
console.log(`Received "${price}" in renderer process.`);
});

Webview Preload Script

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
</head>
<script>
require('./renderer.js');
</script>
<body>
<webview
preload="./preload.js"
src="https://benny.work"
></webview>
</body>
</html>

You can assign a preload script programmatically:

1
2
3
4
5
6
7
8
9
import fileUrl = require('file-url');

const main = new BrowserWindow();

const contents = main.webContents;

contents.on('will-attach-webview', (event, webPreferences, params) => {
webPreferences.preloadURL = fileUrl('./preload.js');
});

preload.js

1
2
3
4
5
6
7
8
9
10
11
12
13
const {ipcRenderer} = require('electron');

window.addEventListener('DOMContentLoaded', () => {
// From guest (webview content) to host (main process)
window.addEventListener(z.event.WebApp.LIFECYCLE.RESTART, (event) => {
ipcRenderer.send(EVENT_TYPE.WRAPPER.RELAUNCH);
});

// From host (main process) to guest (webview content)
ipcRenderer.on(EVENT_TYPE.WRAPPER.RELAUNCHED, () => {
window.dispatchEvent(new CustomEvent(EVENT_TYPE.ACTION.CREATE_ACCOUNT));
});
});

main.ts

1
2
3
4
5
6
7
8
9
10
11
import {BrowserWindow} from 'electron';

const main = new BrowserWindow();
main.loadFile('index.html');
ipcMain.on(
EVENT_TYPE.WRAPPER.RELAUNCH,
async (event: IpcMessageEvent) => {
console.log('Do some work and send a reply...');
main.webContents.send(EVENT_TYPE.WRAPPER.RELAUNCHED);
}
);