Building Cross-Platform Desktop Applications with Electron and NW.js

Node.js is often known as a programming framework for web applications and even robotics, but one of the surprising offshoots to emerge in recent times are desktop applications built on top of Node.js. Applications like GitHub’s Atom editor, Slack, Microsoft’s Visual Studio Code and Popcorn Time have all been created with Node.js desktop application frameworks.

In the space, there are two main frameworks: NW.js (sponsored by Intel), and Electron (sponsored by GitHub). In this article I’ll describe to you the benefits of building desktop applications with Node.js, how both of the frameworks work, how they differ in their approaches to building desktop applications, and highlight a few more of the real-world examples out there in the wild.

Why build desktop applications with Node.js?

In the past building desktop applications has required developers to know languages like C++, Objective-C and C#, as well as understanding application frameworks like Qt, Cocoa or .net. For some developers this has been a barrier to them making desktop applications. With Node.js, designers and developers who are familiar with Javascript have a way into building server-side applications. NW.js and Electron enable those same web designers and developers to also build desktop applications. Not only that, they can build those desktop applications using the same skills and even the same code that they use for their web sites and applications.

Another benefit is that NW.js and Electron allow you to create standalone executables of your application for Windows, Mac OS X and Linux, all from a single codebase. This can save developers a lot of time as they don’t need to create and maintain multiple versions of the application in different codebases, and as you’re able to reuse code from web sites and applications in the app, it massively improves code reuse across your applications.

Finally, being able to use Node.js in your desktop applications means being able to install and use libraries that are available from NPM. At almost 250,000 libraries to date, you’ll most likely to be able to find whatever you need for building your application’s features.

Besides those reasons, desktop applications are a good choice for applications that need to run on a computer even if the Internet is not available, as well as being able to have a better level of control over the user’s experience with the application, and not having to worry about browser feature support. We’ll start to take a look at how NW.js works.

Introducing NW.js

NW.js is a framework for building desktop applications with HTML, CSS, and JavaScript. It was created by Roger Wang at Intel’s Open Source Technology Center in China, and worked by combining the Node.js programming framework with Chromium’s (then) browser engine — Webkit, hence the original name Node Webkit.

By combining Node.js with Chromium, Roger found a way to create applications that could not only load a local web site within an application window, but also could interact with the Operating System via a JavaScript API. This JavaScript API could control visual aspects like window dimensions, toolbar and menu items, as well as provide access to local files on the desktop. These are things that can’t be done with a hosted web site, or even a locally hosted web site. Below is an example of how an example application works. Below is the code for 2 files that make up an NW.js application:

index.html

<html>
<head>
<title>Hello World</title>
<style>
body {
background-image: linear-gradient(45deg, #EAD790 0%, #EF8C53 100%);
text-align: center;
}
    button {
background: rgba(0,0,0,0.40);
box-shadow: 0px 0px 4px 0px rgba(0,0,0,0.50);
border-radius: 8px;
color: white;
padding: 1em 2em;
border: none;
font-family: ‘Roboto’, sans-serif;
font-weight: 300;
font-size: 14pt;
position: relative;
top: 40%;
cursor: pointer;
outline: none;
}
    button:hover {
background: rgba(0,0,0,0.30);
}
</style>
<link href=’https://fonts.googleapis.com/css?family=Roboto:300' rel=’stylesheet’ type=’text/css’>
<script>
function sayHello () {
alert(‘Hello World’);
}
</script>
</head>
<body>
<button onclick=”sayHello()”>Say Hello</button>
</body>
</html>

package.json

{
“name” : “hello-world”,
“main” : “index.html”,
“version” : “1.0”
}

In the example code above, the application’s files resemble those of a simple web site. The index.html web page is like most other web pages that you’ve seen. At this stage it’s identical to the code for a website, and if you were to open it in a web browser it would work the same as it would in NW.js. The only difference is that there is a package.json file present. This is the manifest file used by Node.js to load applications and libraries, and NW.js uses it to store configuration information about the desktop application. It is required by NW.js for the application to load.

Running NW.js from the command line on Mac OS X

The NW.js application is able to load from the command line with a given path of the folder where the files live. It looks for the package.json file and finds the “main” property in the JSON, which points to the index.html file. This indicates to NW.js to load the index.html file for the application and thus loads the web page into what looks like a web browser embedded inside of an application window. This is what you can expect to see:

The Hello World NW.js app on Mac OS X

And on Windows:

The same NW.js hello world app running on Windows 10

And on Linux:

The NW.js hello world app running on OpenSUSE Linux 13.2

The example application above could load inside of a web browser without any modifications, but where NW.js differs from a web browser is that where the Javascript loaded or referenced inside the index.html’s contents can only interact with the web page, NW.js allows the app.js file to interact with the Operating System through a custom JavaScript API, as well as through Node.js. This is unlike web frameworks where the front-end code and the back-end code traditionally exist and execute in separate places.

In web applications, the back-end code is running from a server, and the page that is delivered to the browser on the user’s computer is limited as to what it can do on a user’s computer, due to the browser’s content security policy. With an NW.js desktop app, because the user has explicitly executed the application and it is running in a local context, then the content security does not apply. Also, the application has access to both the page as well as the computer’s resources, through an API to interact with the Operating System, allowing the code to interact not only with the front-end part of the application, but also the back-end part of the application (the computer in this case as no external server serves the desktop app).

For example, here’s another application that is reading the content’s the Home folder on my computer, and listing them inside the application:

A File Explorer application displaying the contents of a user’s folder

This is one of the aspects of NW.js that makes it so unique as a desktop application framework. In the next section we’ll cover Electron, a recent but very popular framework from GitHub.

Introducing Electron

Electron is a desktop application framework from Github. It was built for their text editor Atom, and was originally known as Atom Shell. It allows you to build cross-platform desktop applications using HTML, CSS, and Javascript. Although it was released back in 2013, it has become very popular and is used by a number of startups and large businesses for their applications. Apart from Github’s Atom editor, the other major app using Electron is Slack, the chat application.

In many ways Electron is very similar to NW.js; one of the early contributors to NW.js (Cheng Zhao) is a core contributor to Electron, and works for Github in Beijing. Both frameworks combine Chromium and Node.js to allow you to build cross-platform desktop applications, and both have similar feature sets.

That said, Electron takes a slightly different approach to how applications are initialised, as well as how Chromium is integrated into the framework. To get a better understanding of this, let’s walk through an example of how Electron loads an application. Below is the same kind of “hello world” application shown earlier for NW.js, but running with Electron:

main.js

‘use strict’;
var electron = require(‘electron’);
var app = electron.app;
var BrowserWindow = electron.BrowserWindow;
var mainWindow = null;
app.on(‘window-all-closed’, function () {
if (process.platform !== ‘darwin’) app.quit();
});
app.on(‘ready’, function () {
mainWindow = new BrowserWindow();
mainWindow.loadURL(‘file://’ + process.cwd() + ‘/index.html’);
mainWindow.on(‘closed’, function () { mainWindow = null; });
});

index.html

<html>
<head>
<title>Hello World</title>
<style>
body {
background-image: linear-gradient(45deg, #EAD790 0%, #EF8C53 100%);
text-align: center;
}
    button {
background: rgba(0,0,0,0.40);
box-shadow: 0px 0px 4px 0px rgba(0,0,0,0.50);
border-radius: 8px;
color: white;
padding: 1em 2em;
border: none;
font-family: ‘Roboto’, sans-serif;
font-weight: 300;
font-size: 14pt;
position: relative;
top: 40%;
cursor: pointer;
outline: none;
}
    button:hover {
background: rgba(0,0,0,0.30);
}
</style>
<link href=’https://fonts.googleapis.com/css?family=Roboto:300' rel=’stylesheet’ type=’text/css’>
<script>
function sayHello () {
alert(‘Hello World’);
}
</script>
</head>
<body>
<button onclick=”sayHello()”>Say Hello</button>
</body>
</html>

package.json

{
“name” : “hello-world”,
“version” : “0.0.1”,
“main” : “main.js”
}

One of the things that you’ll notice from the Electron version of the hello world example is that there are 3 files rather than 2. This is because where NW.js uses the index.html file as the 1st file to load for an application, Electron specifies a Javascript file as the 1st file to load for an application. If you look at the package.json file, you’ll see that the “main” property in the JSON refers to the main.js file as the point of entry.

Looking at the contents of the main.js file, we can see as well that there is a line where the application creates a new window and then loads the index.html file into it. This is how the index.html file is loaded by the application. The end result looks like this:

The same app running with Electron on Mac OS X

and on Windows:

The same hello world app running with Electron on Windows 10

and on Linux:

The same hello world app running with Electron on OpenSUSE Linux 13.2

Although it seems like there is a bit more coding involved in getting an application to load in Electron, the benefit is that there is a lot more flexibility in how the application loads app windows, as well as easier support for creating Tray applications. This also fits with the approach that Electron takes to handling state in the back-end code and front-end code. Unlike NW.js, they are kept separate, but data can be passed between the front-end and back-end via inter-process communication, as illustrated in the diagram below:

How Electron runs the back-end process and application windows as separate processes, with Inter-Process Communication used to communicate data between the processes

Although this approach involves more code and data communication, it makes it easier to track how state is being altered on the front-end and back-end parts of the application.

### What other features do NW.js and Electron offer?

Besides being able to create desktop applications with HTML, CSS and Javascript, NW.js and Electron’s use of Chromium offers the ability to add nice features in your application, such as:

- Webcam/microphone access to the computer
- Read and write files on the user’s computer
- Native UI elements such as menus
- Tray applications
- Get and set text and other contents to the clipboard
- Create desktop notifications
- Bind to keyboard shortcuts
- Create apps that run in full screen/kiosk mode

Some of the applications using NW.js and Electron out there today include:

- Slack, the group chat application
- Gitter, another chat application, for developers
- Atom, Github’s IDE
- Microsoft’s Visual Studio Code, a cross-platform IDE
- Facebook’s Nuclide, a fork of Atom tailored for Facebook’s technologies
- Docker’s Kitematic, a GUI for Docker containers
- SoundNode — SoundCloud for desktop

This gives you a flavour of what can be done with NW.js and Electron.

Cross-Platform Desktop Applications with Node, Electron and NW.js

If you’re interested to find out more, check out my book “Cross-Platform Desktop Applications with Node, Electron, and NW.js”.

Like what you read? Give Paul Jensen a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.