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
const {desktopCapturer, ipcRenderer, webFrame} = require('electron');
2
const {app} = require('electron').remote
3
4
webFrame.setZoomFactor(1.0);
5
webFrame.setVisualZoomLevelLimits(1, 1);
6
7
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
import {ipcRenderer} from 'electron';
2
3
const updateBtn = document.getElementById('updateBtn')
4
5
updateBtn.addEventListener('click', () => {
6
  ipcRenderer.send('my-app-event', document.getElementById('notifyVal').value);
7
});

main.ts

1
import {BrowserWindow, ipcMain, IpcMessageEvent} from 'electron';
2
3
const main = new BrowserWindow();
4
5
ipcMain.on('my-app-event', (event: IpcMessageEvent, price: number) => {
6
  main.webContents.send('target-price', price);
7
});

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
import {remote} from 'electron';
2
3
// ...
4
5
remote.ipcMain.on('my-app-event', (event, price) => {
6
  console.log(`Received "${price}" in renderer process.`);
7
});

Webview Preload Script

index.html

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

You can assign a preload script programmatically:

1
import fileUrl = require('file-url');
2
3
const main = new BrowserWindow();
4
5
const contents = main.webContents;
6
7
contents.on('will-attach-webview', (event, webPreferences, params) => {
8
  webPreferences.preloadURL = fileUrl('./preload.js');
9
});

preload.js

1
const {ipcRenderer} = require('electron');
2
3
window.addEventListener('DOMContentLoaded', () => {
4
  // From guest (webview content) to host (main process)
5
  window.addEventListener(z.event.WebApp.LIFECYCLE.RESTART, (event) => {
6
    ipcRenderer.send(EVENT_TYPE.WRAPPER.RELAUNCH);
7
  });
8
  
9
  // From host (main process) to guest (webview content)
10
  ipcRenderer.on(EVENT_TYPE.WRAPPER.RELAUNCHED, () => {
11
    window.dispatchEvent(new CustomEvent(EVENT_TYPE.ACTION.CREATE_ACCOUNT));
12
  });
13
});

main.ts

1
import {BrowserWindow} from 'electron';
2
3
const main = new BrowserWindow();
4
main.loadFile('index.html');
5
ipcMain.on(
6
  EVENT_TYPE.WRAPPER.RELAUNCH,
7
  async (event: IpcMessageEvent) => {
8
    console.log('Do some work and send a reply...');
9
    main.webContents.send(EVENT_TYPE.WRAPPER.RELAUNCHED);
10
  }
11
);