In this article by Alessandro Benoit, author of the book NW.js Essentials, we will learn that until a while ago, developing a desktop application that was compatible with the most common operating systems required an enormous amount of expertise, different programming languages, and logics for each platform.
(For more resources related to this topic, see here.)
Yet, for a while now, the evolution of web technologies has brought to our browsers many web applications that have nothing to envy from their desktop alternative. Just think of Google apps such as Gmail and Calendar, which, for many, have definitely replaced the need for a local mail client. All of this has been made possible thanks to the amazing potential of the latest implementations of the Browser Web API combined with the incredible flexibility and speed of the latest server technologies.
Although we live in a world increasingly interconnected and dependent on the Internet, there is still the need for developing desktop applications for a number of reasons:
Once it's established that we cannot completely get rid of desktop applications and that their implementation on different platforms requires an often prohibitive learning curve, it comes naturally to ask: why not make desktop applications out of the very same technologies used in web development?
The answer, or at least one of the answers, is NW.js!
NW.js doesn't need any introduction. With more than 20,000 stars on GitHub (in the top four hottest C++ projects of the repository-hosting service) NW.js is definitely one of the most promising projects to create desktop applications with web technologies. Paraphrasing the description on GitHub, NW.js is a web app runtime that allows the browser DOM to access Node.js modules directly.
Node.js is responsible for hardware and operating system interaction, while the browser serves the graphic interface and implements all the functionalities typical of web applications. Clearly, the use of the two technologies may overlap; for example, if we were to make an asynchronous call to the API of an online service, we could use either a Node.js HTTP client or an XMLHttpRequest Ajax call inside the browser.
Without going into technical details, in order to create desktop applications with NW.js, all you need is a decent understanding of Node.js and some expertise in developing HTML5 web apps.
In this article, we are going to dissect the topic dwelling on these points:
Important notes about NW.js (also known as Node-Webkit) and io.js
Before January 2015, since the project was born, NW.js was known as Node-Webkit. Moreover, with Node.js getting a little sluggish, much to the concern of V8 JavaScript engine updates, from version 0.12.0, NW.js is not based on Node.js but on io.js, an npm-compatible platform originally based on Node.js. For the sake of simplicity, we will keep referring to Node.js even when talking about io.js as long as this does not affect a proper comprehension of the subject.
As we stated in the introduction, NW.js, made by Roger Wang of Intel's Open Source Technology Center (Shanghai office) in 2011, is a web app runtime based on Node.js and the Chromium open source browser project. To understand how it works, we must first analyze its two components:
Since the browser, for security reasons, cannot access the application layer and since Node.js lacks a graphical interface, Roger Wang had the insight of combining the two technologies by creating NW.js.
The following is a simple diagram that shows how Node.js has been combined with WebKit in order to give NW.js applications access to both the GUI and the operating system:
In order to integrate the two systems, which, despite speaking the same language, are very different, a couple of tricks have been adopted. In the first place, since they are both event-driven (following a logic of action/reaction rather than a stream of operations), the event processing has been unified. Secondly, the Node context was injected into WebKit so that it can access it.
The amazing thing about it is that you'll be able to program all of your applications' logic in JavaScript with no concerns about where Node.js ends and WebKit begins.
Today, NW.js has reached version 0.12.0 and, although still young, is one of the most promising web app runtimes to develop desktop applications adopting web technologies.
Let's check some of the features that characterize NW.js:
However, all that glitters is not gold. There are some cons to consider when developing an application with NW.js:
The flexibility and good performance of NW.js allows its use in countless scenarios, but, for convenience, I'm going to report only a few notable ones:
The choice of development platform for a new project clearly depends only on the developer; for the overall aim of confronting facts, it may be useful to consider some specific scenarios where the use of NW.js might not be recommended:
After summarizing the pros and cons of NW.js, let's not forget the real strength of the platform—the many applications built on top of NW.js that have already been distributed. We list a few that are worth noting:
Installing NW.js is pretty simple, but there are many ways to do it. One of the easiest ways is probably to run npm install nw from your terminal, but for the educational purposes, we're going to manually download and install it in order to properly understand how it works.
You can find all the download links on the project website at http://nwjs.io/ or in the Downloads section on the GitHub project page at https://github.com/nwjs/nw.js/; from here, download the package that fits your operating system.
For example, as I'm writing this article, Node-Webkit is at version 0.12.0, and my operating system is Mac OS X Yosemite 10.10 running on a 64-bit MacBook Pro; so, I'm going to download the nwjs-v0.12.0-osx-x64.zip file.
Packages for Mac and Windows are zipped, while those for Linux are in the tar.gz format. Decompress the files and proceed, depending on your operating system, as follows.
Inside the archive, we're going to find three files:
Before v0.12.0, the filename of nwjc was nwsnapshot.
Currently, the only file that interests us is nwjs.app (the extension might not be displayed depending on the OS configuration). All we have to do is copy this file in the /Applications folder—your main applications folder.
If you'd rather install NW.js using Homebrew Cask, you can simply enter the following command in your terminal:
$ brew cask install nwIf you are using Homebrew Cask to install NW.js, keep in mind that the Cask repository might not be updated and that the nwjs.app file will be copied in ~/Applications, while a symlink will be created in the /Applications folder.
Inside the Microsoft Windows NW.js package, we will find the following files:
Some of the files in the folder will be omitted during the final distribution of our application, but for development purposes, we are simply going to copy the whole content of the folder to C:/Tools/nwjs.
On Linux, the procedure can be more complex depending on the distribution you use. First, copy the downloaded archive into your home folder if you have not already done so, and then open the terminal and type the following command to unpack the archive (change the version accordingly to the one downloaded):
$ gzip -dc nwjs-v0.12.0-linux-x64.tar.gz | tar xf -
Now, rename the newly created folder in nwjs with the following command:
$ mv ~/nwjs-v0.12.0-linux-x64 ~/nwjs
Inside the nwjs folder, we will find the following files:
Open the folder inside the terminal and try to run NW.js by typing the following:
$ cd nwjs
$ ./nw
If you get the following error, you are probably using a version of Ubuntu later than 13.04, Fedora later than 18, or another Linux distribution that uses libudev.so.1 instead of libudev.so.0: otherwise, you're good to go to the next step:
error while loading shared libraries: libudev.so.0: cannot
open shared object file: No such file or directory
Until NW.js is updated to support libudev.so.1, there are several solutions to solve the problem. For me, the easiest solution is to type the following terminal command inside the directory containing nw:
$ sed -i 's/udev.so.0/udev.so.1/g' nw
This will replace the string related to libudev, within the application code, with the new version. The process may take a while, so wait for the terminal to return the cursor before attempting to enter the following:
$ ./nw
Eventually, the NW.js window should open properly.
As you'll make use of third-party modules of Node.js, you're going to need npm in order to download and install all the dependencies; so, Node.js (http://nodejs.org/) or io.js (https://iojs.org/) must be obviously installed in your development environment.
I know you cannot wait to write your first application, but before you start, I would like to introduce you to Sublime Text 2. It is a simple but sophisticated IDE, which, thanks to the support for custom build scripts, allows you to run (and debug) NW.js applications from inside the editor itself.
If I wasn't convincing and you'd rather keep using your favorite IDE, you can skip to the next section; otherwise, follow these steps to install and configure Sublime Text 2:
{
"cmd": ["nwjs", "--enable-logging", "${project_path:${file_path}}"],
"working_dir": "${project_path:${file_path}}",
"path": "/Applications/nwjs.app/Contents/MacOS/"
}
{
"cmd": ["nw.exe", "--enable-logging", "${project_path:${file_path}}"],
"working_dir": "${project_path:${file_path}}",
"path": "C:/Tools/nwjs/",
"shell": true
}
{
"cmd": ["nw", "--enable-logging", "${project_path:${file_path}}"],
"working_dir": "${project_path:${file_path}}",
"path": "/home/userName/nwjs/"
}
Perfect! Now you are ready to run your applications directly from the IDE.
There are a lot of packages, such as SublimeLinter, LiveReload, and Node.js code completion, available to Sublime Text 2. In order to install them, you have to install Package Control first. Just open https://sublime.wbond.net/installation and follow the instructions.
Finally, we are ready to write our first simple application. We're going to revisit the usual "Hello World" application by making use of a Node.js module for markdown parsing.
"Markdown is a plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name."
– Wikipedia
Let's create a Hello World folder and open it in Sublime Text 2 or in your favorite IDE. Now open a new package.json file and type in the following JSON code:
{
"name": "nw-hello-world",
"main": "index.html",
"dependencies": {
"markdown": "0.5.x"
}
}
The package.json manifest file is essential for distribution as it determines many of the window properties and primary information about the application. Moreover, during the development process, you'll be able to declare all of the dependencies.
In this specific case, we are going to assign the application name, the main file, and obviously our dependency, the markdown module, written by Dominic Baggott.
If you so wish, you can create the package.json manifest file using the npm init command from the terminal as you're probably used to already when creating npm packages.
Once you've saved the package.json file, create an index.html file that will be used as the main application file and type in the following code:
<!DOCTYPE html>
<html>
<head>
<title>Hello World!</title>
</head>
<body>
<script>
<!--Here goes your code-->
</script>
</body>
</html>
As you can see, it's a very common HTML5 boilerplate. Inside the script tag, let's add the following:
var markdown = require("markdown").markdown,
div = document.createElement("div"),
content = "#Hello World!n" +
"We are using **io.js** " +
"version *" + process.version + "*";
div.innerHTML = markdown.toHTML(content);
document.body.appendChild(div);
What we do here is require the markdown module and then parse the content variable through it. To keep it as simple as possible, I've been using Vanilla JavaScript to output the parsed HTML to the screen. In the highlighted line of code, you may have noticed that we are using process.version, a property that is a part of the Node.js context.
If you try to open index.html in a browser, you'd get the Reference Error: require is not defined error as Node.js has not been injected into the WebKit process.
Once you have saved the index.html file, all that is left is to install the dependencies by running the following command from the terminal inside the project folder:
$ npm install
And we are ready to run our first application!
If you opted for Sublime Text 2 and followed the procedure in the development tools section, simply navigate to Project | Save Project As and save the hello-world.sublime-project file inside the project folder.
Now, in the top menu, navigate to Tools | Build System and select nw-js. Finally, press Ctrl + B (or Cmd + B on Mac) to run the program.
If you have opted for a different IDE, just follow the upcoming steps depending on your operating system.
Open the command prompt and type:
C:> c:Toolsnwjsnw.exe c:pathtotheproject
On Microsoft Windows, you can also drag the folder containing package.json to nw.exe in order to open it.
Open the terminal and type:
$ /Applications/nwjs.app/Contents/MacOS/nwjs /path/to/the/project/
Or, if running NW.js applications inside the directory containing package.json, type:
$ /Applications/nwjs.app/Contents/MacOS/nwjs.
As you can see in Mac OS X, the NW.js kit's executable binary is in a hidden directory within the .app file.
Open the terminal and type:
$ ~/nwjs/nw /path/to/the/project/
Or, if running NW.js applications inside the directory containing package.json, type:
$ ~/nwjs/nw .
Running the application, you may notice that a few errors are thrown depending on your platform. As I stated in the pros and cons section, NW.js is still young, so that's quite normal, and probably we're talking about minor issues. However, you can search in the NW.js GitHub issues page in order to check whether they've already been reported; otherwise, open a new issue—your help would be much appreciated.
Now, regardless of the operating system, a window similar to the following one should appear:
As illustrated, the process.version object variable has been printed properly as Node.js has correctly been injected and can be accessed from the DOM.
Perhaps, the result is a little different than what you expected since the top navigation bar of Chromium is visible. Do not worry! You can get rid of the navigation bar at any time simply by adding the window.toolbar = false parameter to the manifest file, but for now, it's important that the bar is visible in order to debug the application.
In this article, you discovered how NW.js works under the hood, the recommended tools for development, a few usage scenarios of the library, and eventually, how to run your first, simple application using third-party modules of Node.js. I really hope I haven't bored you too much with the theoretical concepts underlying the functioning of NW.js; I really did my best to keep it short.