Using Web Technologies to create your first Cross-Platform Desktop Application with Electron

Tushar Arora
Nerd For Tech
Published in
10 min readFeb 27, 2021

--

React and Electron

Having build Websites during all my years of experience, I have to admit, creating a piece of software that runs on a Desktop Environment sounded tedious to me as I always thought I had to learn about a whole new toolset.

But when I came across Electron, I realized that Desktop App development isn’t as intimidating as It once was.

What is Electron? How can a subatomic particle help me develop Softwares?

First of all, when I say Electron, I am referring to the Open Source Software Framework that allows for the development of desktop GUI applications using web technologies like HTML, CSS, JavaScript, etc.

Why Electron?

Cross-Platform

There are many tools out there that can help you develop Softwares that run on different Operating Systems but only a few are capable of providing cross-platform compatibility. Thankfully, Electron does, which is one of the main reasons It became so popular, along with allowing Web Developers to use their Website Building Experience for Software Development.

Reusability

Since we are using web technologies, the same source code for the UI can be deployed as a well-functioning Website on any service you like. So we are giving importance to the principle Code Once, Use Everywhere.

Security

From large-scale web-based Softwares like Microsoft Teams to Code Editors like Visual Studio Code and Atom are developed in Electron. If that’s not a testament to the level of Security Electron provides, I don’t know what is.

Performance

If proper care is taken while developing (load only what you need), Electron can show some great gains in the terms of performance when compared to native applications.

Cost and Time

We are using our experience as Web Developers to develop Desktop Apps, which saves us time to learn about various desktop application development Tools and start from scratch, and also helps us skip over different Native Features of different Platforms.

Electron’s Architecture? A Blackbox?

Architecture

Let’s see how Electron works.

  1. When you launch an electron application, a request is made to open the window Object(the one you see in the front-end) by the main process.
  2. The Window Object or commonly known as the Renderer process when ready/programmatically defined, loads up.

No Desktop Application is complete without Native Features so here’s how it would work in an Electron App.

  1. An event is raised by the renderer process in the front end.
  2. The Renderer process emits the event to the main process(coded in NodeJS).
  3. The Main Process constantly listens for various events, in this case, let’s assume we have coded the event to write a file to the local file system.
  4. The Main process upon receiving the event, acts on it by accessing the Native APIs which in our example, will write to the computer file system.

Popular Software built with Electron

  1. Visual Studio Code (VS Code)
  2. Slack
  3. Tusk
  4. Mailspring
  5. Skype
  6. Discord (My Personal Favorite due to the wide array of features)
  7. WhatsApp Desktop
  8. Atom

How do we do it?

Now that I have gone over the basics of how Electron works and why you should spend time mastering it.

Now we must use those basics to actually develop something that we can use. That means We start coding now.

Prerequisites

  1. NodeJS
  2. Code Editor // I use VS Code
  3. Some Experience in NodeJS and Front-End Development
  4. A Happy and Fresh Mind

Getting Started

I am assuming you have fulfilled all the prerequisites of the tutorial, especially the last one along with NodeJS and your Code Editor installations.

You can verify NodeJS installation by typing the following commands in your terminal

node -v
npm -v

If the above commands do not print out the version of NodeJS and NPM, you need to go through their official documentation and install it before you go any further.

So now we need to visit Electron’s Official Website for some basic information like how to set up electron locally. But It is optional as I will be going over it step by step.

Create the Project

You need to create a directory in your computer that will hold the source code of the project, I will only be using terminals for such tasks so You can perform the following command to create the directory and move in it

mkdir electron-demo
cd electron-demo

Where is Electron?

Before you install Electron, you need to initialize the project with the following command.

npm init -y

The above command will create a basic package.json file in your root directory. We will be using that file to create scripts and install npm packages in our project as we progress further into this tutorial.

Now we need to install Electron into the project by typing the following command into the terminal

npm i --save-dev electron

This command will generate the folder called node_modules and a package-lock.json in your project root directory. You can verify the installation of Electron by going into node_modules and looking for electron directory.

The --save-dev flag installs Electron as a dev dependency. Why we did that? Because when We package an application, Electron is no longer required for it to run.

Coding the Application

Configuring main.js

main.js is the file which that serves as the entry point to our Electron Application. But main.js is just a name, and also the convention because It will run the Main Process which controls the lifecycle of the application and accesses Native APIs.
You can name it whatever you want, only thing is to specify the main file in package.json But remember, there can only be one main file.
So, let’s create a main.js in your project root, and put the following code into it.

Basic Electron Code

Now, Let’s go over what is there in the code.

  • line 1: we are importing app and BrowserWindow from Electron package. app is the module which controls the application’s event lifecycle and BrowserWindow creates and manages the Windows we see in UI. Individually you can go to app and BrowserWindow documentation to explore them and their events.
  • line 5: we are creating a BrowserWindow with the options we have given. Leaving the ones which are self-explanatory, let’s go over nodeIntegration and enableRemoteModule.
    1. nodeIntegration: Allows the BrowserWindow to access NodeJS API.
    2. enableRemoteModule: Enables the BrowserWindow to access
    the remote module which to summarize helps establish communication between the main process and the renderer process.
  • line 16: We are loading the index.html file inside the window. We will create that file next.
  • line 18: We are telling the window to quit app when the window itself closes.
  • line 23: We are telling app to create the window when it finishes loading.
  • line 25: We are adding a new listener that tries to quit the application when it no longer has any open windows. This is not required in macOS, so the if statement.
  • line 31: We are adding a new listener that creates a new browser window only if when the application has no visible windows after being activated. For example, after launching the application for the first time, or re-launching the already running application.

Creating index.html

Now we need to create the file which we will be seeing in the BrowserWindow. In our case, It is index.html as defined in Line 16 when we created main.js.

Basic Electron HTML

In the Code above, Let’s focus on how we are printing the RAM and also the version of Chrome on which the browser window is running.
The script tag is printing the information with the process module, which is only available to the BrowserWindow Object because we enabled nodeIntegration while writing main.js above.

Now that we have the back-end and front-end set up, How do we see the result?

  1. For that, we need to add a script in package.json . So open it and inside the scripts add a new entry "start" : "electron ."
  2. We also need to point to our entry point NodeJS file, which in our project is main.js . So we modify "main": "main.js".

So package.json will start looking like below.

Now, all we need to do is run the script, which we can do by going into the terminal, navigating to the root directory, and typing the following command.

npm start

If everything is correct, We should see the following screen pop up.

Basic Electron Window with index.html

Helpful Tip — We have programmed our main process to load the file called index.html but We could also have a pre-existing website load directly into the Window by window.loadURL('https://google.com') instead of window.loadFile('index.html') in main.js. So we don’t need to write extensive code to port our websites into a Desktop Application. Electron got you covered.

Loading Websites directly into the BrowserWindow

But for the sake of experimenting, We will keep it at window.loadFile('index.html').

Inter-Process Communication

It is not a desktop application if it doesn’t interact with the native APIs.
So we will perform a basic operation to showcase Electron’s ability in that regard.
We can do that with IPC (Inter-Process Communication) modules.
The main process and the renderer processes use ipcMain and ipcRenderer modules respectively.

The flow we will follow goes as:

  1. An Event occurs in BrowserWindow which emits a signal along with optional data to the main process.
  2. The Main process in response to the event signal, may return some data back to the renderer process(BrowserWindow) or perform operations in the back-end or both.

NOTE — This is possible because we enabled Remote Module in main.js when we were creating the BrowserWindow.

Let’s see this in action.

index.html with IPC Events

In the code above, let’s focus on the content inside <script>..</script> tag.

  1. We are loading in the ipcRenderer module from the electron package, which is only available to us because we have enabled remoteModule in main.js .
  2. We are adding an click event listener to a button in the HTML which will emit a signal called performActions for the main process to listen.
  3. We are also sending an object that contains the two input fields of firstName and lastName as an argument upon which the main process will perform desired actions.

Now, where is the event being handled? We do that in main.js .

Understand the new code? Well, Let me explain it to you anyway.

  1. The first difference compared to previous main.js is that now we are importing 2 additional packages, known as ipcMain and dialog .
  2. ipcMain comes into action in line 36 when we add an event listener(performActions) to it.
  3. In my example of using native functionality, I am using the data I received from the form (firstName & lastName), and creating a native dialog box that appears in the BrowserWindow.

NOTE— To Display a dialog is a User Choice, I wanted to keep the tutorial as simple and easy to understand as possible.

A Practical Example would be to use NodeJS’s fs module to store the data somewhere on the local file system and send it to BrowserWindow on the first launch or upon any other event.

IPC Result

How is a dialog different from a modal?

Well, a Modal is a custom CSS-made element that stays the same for all the platforms. But a dialog created by accessing the Native APIs will change depending on the Operating System.

There are too many Native Modules for me to go over so You can click here to explore them all in the official Documentations.

There are multiple types of dialog boxes in a System, you can explore them here.

This concludes the tutorial on Using Web Technologies to create your first Cross-Platform Desktop Application with Electron

You can also visit the Github Repository for this project to view the complete Source Code.

Alternatives to Electron?

Flutter

Pros

  1. Open Source
  2. Great Number of features like Debugging tools, Hot reload
  3. Based on Dart Language

Cons

  1. Still in Development
  2. Underwhelming Community Support compared to Java or Kotlin

B4X

Pros

  1. Great Community
  2. Easy and Fast
  3. Live Code Swapping // Hot Reload
  4. Very Powerful for IoT // Support for Bluetooth/BLE, MQTT, TCP/UDP, Serial, NFC, Websockets, HTTP/2 across platforms makes it the ideal tool for IoT projects

Cons

  1. Java Dependent Desktop Environment

wxWidgets

Pros

  1. Mature
  2. Truly Native Look and Feel
  3. Open Source

Cons

  1. Heavy Compiled Result

Proton Native

Pros

  1. Very Fast
  2. React Style
  3. Fully Cross-Platform Compatible
  4. Lightweight
  5. Code reusable with React Native Apps
  6. Native Components

Cons

  1. Community Support I found to be very dry compared to Electron.
  2. The number of Components is very less at this moment.

Other Tools

  1. JavaFx
  2. NodeGUI
  3. React Desktop
  4. Swing

What’s next?

Now we can explore the documentation to experience the full toolset.

Since NodeJS is the language service for the main process, you can combine it with Front End Technologies like React, Angular to create comprehensive Full Stack Desktop Applications that give you the look and feel of a Modern-Day Customized Application along with a reliable and function-rich Native Environment by fully utilizing the potential of NodeJS and the NPM Library.

You can also learn to package your application and making it available publically for everybody to use by visiting the official documentation on distribution.

If You liked this article, please show appreciation by either Clapping or commenting or both. More to come.

You can also go to my Github Profile and view my work, or visit my LinkedIn and connect with me personally if you have any doubts or feedback regarding this article or anything else.

Happy Coding…

--

--

Tushar Arora
Nerd For Tech

Full Stack Engineer | CTO Function | Mindtree Ltd.