NW.js, previously known as node-webkit, is among the community’s favorite Electron alternatives for native application development. Like its competitor, NW.js also enables the easy setup of cross-platform desktop applications simply by using the core trio of HTML, CSS, and JavaScript.
You code as if you’re creating any ordinary web application with your favorite tools (React, Angular, Less, webpack, etc.), and in the end, NW.js takes care of translating everything from the web techs to native ones.
Many apps have adopted NW.js already as their hybrid development framework. And all this is possible due to Chromium, the same open source browser owned by Google that powers not only the company’s web browser, but also dozens of other dependent projects out there.
Apart from that, NW.js offers a range of great integrations, including native OS-based hardware and file system access, integration with the shell, and many more.
In this article, I’m going to walk you through the first steps toward implementing a small web app running on NW.js. Let’s jump right into it!
As mentioned before, since there’s no need for any specific installation or special setup, NW.js gives you the flexibility to opt for whatever environment and tech stack you’d prefer. You can have almost any sort of web application built on top of your favorite framework or library running alongside NW.js.
Plus, because the project doesn’t provide any type of scaffolding tool (except for some specific rules for manifest formats, for example), this tutorial explores how you’d create the project from scratch using the most basic setup: HTML, CSS, and JavaScript only.
So, as a first step, make sure to reproduce the same structure of folders and files demonstrated below:
The most important folder here is the src
, as you may have guessed. That’s where you separate the NW.js main files. Actually, everything related to NW.js belongs to this file.
For some of the auxiliary files such as images and favicons, you can add whatever you like.
Note also that there are two package.json
files. That happens because a separate src/package.json
may be created to store the manifest configs, as stated here. The other is just the ordinary package.json
to handle the project dependencies and build commands.
Speaking of the package.json
file, its contents are below:
{ "name": "logrocket_nwjs_example", "version": "1.0.0", "description": "A demo app to show how NW.js works", "main": "index.html", "window": { "icon": "assets/icon.png", "height": 500, "width": 400, "resizable": true, "title": "Hello World, NW.js" }, "dependencies": { "bootstrap": "^4.6.0", "jquery": "^3.6.0" } }
You can change some of the information as you please, but make sure you always have name
and version
attributes since they’re obligatory.
Then, run the following command in the src
folder to download the required dependencies:
yarn install
This command will download the Bootstrap and jQuery dependencies since you’ll need them to create a better look and feel.
The width
and height
correspond to the dimensions of your app screen when it first starts up, while the resizable
property is important to configure if you allow users to resize the app window.
The next stop is the index.html
content. Ddd the following HTML code into it:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>NW.js-example | main</title> <link rel="stylesheet" href="css/style.css" /> </head> <body class="text-center"> <form> <img class="mb-4" src="https://res.cloudinary.com/practicaldev/image/fetch/s--TXdRGx5X--/c_fill,f_auto,fl_progressive,h_320,q_auto,w_320/https://dev-to-uploads.s3.amazonaws.com/uploads/organization/profile_image/1506/e0a84c58-6a79-4f06-9149-87a38b84afa8.png" alt="" width="72" height="72" /> <h2>Temperature Converter</h2> <p> Type the value you want to convert from Fahrenheit to Celsius and vice versa: </p> <div class="form-group row"> <label for="fahrenheit" class="col-sm-2 col-form-label" >Fahrenheit:</label > <div class="col-sm-10"> <input id="fahrenheit" class="form-control" type="number" placeholder="Fahrenheit" /> </div> </div> <div class="form-group row"> <label for="celsius" class="col-sm-2 col-form-label" >Celsius:</label > <div class="col-sm-10"> <input id="celsius" class="form-control" type="number" placeholder="Celsius" /> </div> </div> </form> <script src="../js/app.js"></script> <script src="node_modules/jquery/dist/jquery.min.js"></script> <script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script> </body> </html>
That’s a basic Bootstrap structure for a form component, with the two temperature fields to convert from Fahrenheit to Celsius and vice versa.
Pay attention to the CSS import at the beginning of the file, and the JavaScript corresponding to Bootstrap, jQuery, and the app’s main JS file, respectively.
The following code belongs to the js/app.js
file:
const celsius = document.getElementById("celsius"); const fahrenheit = document.getElementById("fahrenheit"); fahrenheit.addEventListener("change", (e) => { convertToCelsius(e.target.value); }); celsius.addEventListener("change", (e) => { convertToFahrenheit(e.target.value); }); function convertToCelsius(valNum) { valNum = parseFloat(valNum); celsius.value = (valNum - 32) / 1.8; } function convertToFahrenheit(valNum) { valNum = parseFloat(valNum); fahrenheit.value = valNum * 1.8 + 32; }
They correspond to each of the event listeners for when the fields change their value. They will then trigger the proper conversion and update the field at the end.
The styles are important for the example, too. Aside from those imported from Bootstrap, you need some basic CSS rules to display the content in a centralized way. The following corresponds to the content you should add to the style.css
file:
@import url("../node_modules/bootstrap/dist/css/bootstrap.min.css"); html, body { height: 100%; } body { display: flex; align-items: center; } form { padding: 15px; margin: 0 auto; }
Before testing this implementation as a real NW.js desktop application, it’s worth running the app as an ordinary web app on your browser. You can easily do so by adding the following content to your index.js
file:
const express = require("express"); const path = require("path"); const app = express(); const PORT = process.env.PORT || 3000; app.use(express.static(path.join(__dirname, "src"))); app.listen(PORT, () => { console.log("Server listening on port: %d", PORT); });
Since it is located at the root folder of your project, you can start an Express.js server and serve the static files naturally.
Finally, let’s take a look at the other package.json
file, which is also located in the root project folder. This is the content you should add to it:
{ "name": "logrocket_nwjs_example", "version": "1.0.0", "description": "A demo app to show how NW.js works", "private": true, "scripts": { "start": "node index.js", "start-nw": "nw src/", "build-nw": "nwbuild --platforms win64,osx64,linux64 --buildDir dist/ src/" }, "dependencies": { "express": "^4.17.1", "nw": "^0.52.0", "nw-builder": "^3.5.7" } }
Here, you can see the dependencies related to NW.js itself and the nw-builder, which is a powerful plugin to help with the generation of OS-specific installers.
Make sure to also run the yarn install
command on the root folder to download these npm packages.
After that, run the following command to start the application via the Express server:
yarn start
The app will be available at http://localhost:3000/
. You’ll see the following screen when you access it:
Go ahead and test both fields to check if they’re properly converting and updating the temperatures.
To run the app in NW.js mode, simply execute the following command:
yarn start-nw
This will trigger the NW.js library to generate everything needed for the following screen to show up:
And, finally, in order to generate any OS-specific installer you can run the following command:
yarn build-nw
This command may take a while because it’ll download and install all the required dependencies.
Note that based on the configs of this command — which we set up in the package.json
file — it’ll generate installers for Windows, Linux, and macOS (64 bits) and store them under the dist/
folder of your project.
You mustn’t change any of the project files during the execution of this command; this can lead to further inconsistencies.
Take a look at the output in the image below:
If you access the folder via Finder (on macOS) and execute the logrocket_nwjs_example.app
file, you’ll see the same application start up.
NW.js gives you the flexibility to freely choose whatever tools you’d like to work with. It’s truly great to be able to create hybrid applications by focusing only on the web implementation you’re already familiar with.
Of course, NW.js is not the only Electron alternative out there, but it’s a powerful one if you’re willing to extract the web capabilities into a desktop app. I can’t finish without emphasizing the importance of going through the official docs to learn more about what NW.js is capable of.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowIt’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
2 Replies to "Creating cross-platform apps with NW.js"
Excellent overview of nw.js! I’m thinking of writing an app and possibly using svelte in this Nw.js app. But then again maybe not since I’m also using notion api with this, and there’s a weird conflict between the notion js wrapper, and svelte.. so maybe stick with react for it. I just told you things that you probably care less about. Carls Jr has good fries – that might useful to someone.
express is unnecessary there to test a simple html file. With that kind of html can’t you just the open it in the browser directly? I thought you are showcasing the feature of nwjs to load a server (ssr) application in its window.