This post is the first in my series on Electron and React. This post covers the Electron fundamentals. The second part will cover how a React application fits inside an Electron application.

Posts in this series:

An introduction…

Recently I found myself needing to write a desktop app. I won’t bore you with all of the details, but the setup was like this:

  • I have online services which I need to connect to and fetch some JSON
  • I have a bunch of (sometimes large) local CSV files, which I needed to combine to a single data set with the JSON loaded from the internet
  • My local CSV datasets are short lived, and when time goes on, I need to remove the stale data set and attach a fresh one

In such a scenario, I thought I could go with a classical browser-based app, but juggling datasets over network connection seemed a bit unnecessary, so I decided to write a desktop app.

Choosing the tech was mainly between React Native and Electron, but since I (the Linux user) didn’t want to mess with RN’s Out-of-Tree platforms, but still wanted to do React, I went for Electron. I had done Electron a ways back, but Electron was new at that time and things were a bit messy.

As you would, I started looking for tutorials and documentation: while things had improved since 2014, I found documentation explaining the concepts on a fundamental level somewhat lacking. So, I thought I would finally have something for my tech blog I have been planning since 2010. Let’s see how this goes. Please be kind, its my first day.

How does Electron work?

Node.js and Chromium

An application built with Electron combines two technologies: Node.js and Chromium. Chromium however is not exactly the browser as you might think of it: Electron application runs Chromium as an “embedded” software library.

An Electron app has two types of main components: one “Main Process” and one or more “Browser Windows”.

The Main Process is written in Node.js and can do everything a Node.js application can do, for example access your local files on your hard drive -something a Browser Window cannot do. The Main Process controls the application and opens up Browser Windows.

A Browser Window uses Chromium to render buttons, tables and other bits and bobs of your application. One Main Process can open multiple Browser Windows. The Browser Window, using Chromium, can render HTML while the Main Process can not.

Hold on…
If you are already familiar with the topic and are wondering what I am on about, I know it is not exactly correct to say that the Browser Window cannot read local files, since FileReader exists. But FileReader is somewhat limited, and while my example does use simple file open dialog, there are use cases where the application needs to load files which are not explicitly selected by the user using form inputs. And it made a nice example, so please bear with me. 🙂

Communication between the Main Process and a Browser Window

Since the Main Process and the Browser Window each have unique functionality which the other doesn’t, they often need to communicate: this is done by passing messages between the processes. In my case I needed to load some CSV files from my local disk and send those to my Browser Window, so I could confuse my application users with some complex data tables. This meant:

  1. My Browser Window needed to have a button, which the user could click to load some files from their hard drive
  2. My Main Process needed to be notified about the user’s intent to load CSV files, allow the user to navigate to said files, read the file contents and send them back to the the Browser Window
  3. My Browser Window needed to catch the file contents sent by the Main Process and display them to the user as a table
Electron and React, how does Electron work: the Main Process and Browser Window
Something like this.

The Main Process is written in JavaScript and uses Node.js. The JavaScript code opens a Browser Window using Electron and tells the Browser Window where its index.html and accompanying JavaScript is. Although Chromium is used as a library, it still needs to read and render HTML in order to display buttons and such to the user.

import {BrowserWindow} from 'electron';
...
// Open the browser window
const browserWindow = new BrowserWindow({
    width: 800,
    height: 500
});
// Tell the browser window where to load the index file
browserWindow.loadFile("index.html");

After the Browser Window has read the index.html and rendered the user interface, the user sees a “Load file” button on the screen. A click on the button triggers a JavaScript function, which calls Electron’s Inter-Process Communication methods or IPC. Name is fancy, but it basically means communication between two parties, in this case the Browser Window and the Main Process.

{/*A button to register a click and call method for loading the file*/}
<Button onClick={() => getLocalFile()}>Load file</Button>

JavaScript code running in the Browser Window uses Electron via the browser’s Window object (where it has been injected) to send IPC messages to the Main Process.

function getLocalFile(){
    // Call the main process via IPC to load a file
    const fileContents = await window.electron.ipcHandler.invoke('read-local-file'); 
    // Here we would call appropriate functions to process the fileContents, 
    // for example make a table out of it. 
}

Injecting Electron to the Browser Window
While it is true that the Browser Window gets access to “injected Electron”, this process is not automatic -it is up to the developer to implement. For purposes of keeping this post at reasonable length, though, I will gloss over the injection semantics. The concept used is called “Content Bridge” which I will probably cover in upcoming posts. Additionally, for full disclosure: the term “injected Electron” is not 100% accurate either, but for the sake of this post, it should be close enough. I will cover this in more detail in upcoming posts as well.

Similarly, on the Main Process, JavaScript code uses Electron IPC to catch the message sent by the Browser Window. After catching the message, the code opens up a file selection dialog (again using Electron), which allows the user to choose files from the local hard drive. After the files have been selected, the JavaScript code reads the selected files using Node.js file system API and sends the contents back to the Browser Window as a response. The Browser Window then reads the response, extracts the file contents and renders them as a data table for the user to see (the catching part happened in the snippet above together with the sending of the IPC message).

import {ipcMain, dialog} from 'electron';
import fs from "fs";
...
ipcMain.handle('read-local-file', async (...) => {
    // Open a file dialog
    const response = await dialog.showOpenDialog(...); 
    ...
    // Return the file contents. In this simple example we use the first file the user selected.
    return fs.readFileSync(response.filePaths[0], 'utf8'); 
});
...

Javascript Everywhere!
It is important to not be confused with the JavaScript used to write the Main Process and the JavaScript running in the Browser Window: both are JavaScript, but they operate in vastly different contexts. JavaScript in the Main Process deals with Node.js modules such as: require, or the filesystem package. JavaScript in the Browser Window deals with functions and objects supported by the browser (Chromium), such as the Window object.

Closing thoughts

That’s it. Hopefully a simple enough explanation, which I hope gives you a basic understanding about Electron. I did not cover React here (yet), because I thought it would be important to first understand how Electron renders any HTML, and how the communication between the Main Process and Browser window works on a fundamental level. React, after all, is a JavaScript library which runs in the Browser Window as any JavaScript code would. I’ll come back to this in later posts.

There is more to Electron than I wrote here, but I hope this first post was not too technical. I will cover more Electron (and React) fundamentals in upcoming posts. However, If you feel that you are ready to dive into actual implementation, I would probably recommend something like Electron-React-Boilerplate as a good starting place.

What’s next?

My target is to provide a concrete example which you can use to start your own Electron app. However, before that I am going to talk a bit about React JSX and how browsers can understand something that is not either JavaScript or HTML.

I Would appreciate your feedback! Did I manage to explain the topic well? Did I miss something? Is there a topic you would like to read about? Leave me a comment below – and a big thank you for reading my post!

After commenting, please feel free to continue to Electron and React, Part 2: React JSX and the browser

5 1 vote
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments