A few years back, the only way for web developers to enter into the Desktop app space was either Electron or NW.js. Both choices were not really choices at all --- both were huge, full of bloat, memory hogging frameworks but the solution was tempting to many. Huge companies like Microsoft, Slack, Discord invested into it but there were still many people who did not want to install 9 different versions of Chromium to run 9 different apps.
NeutralinoJS was not a new idea --- instead of packaging the whole Node + Chromium with every app, why not reuse the already installed browser each OS comes with? Linux & macOS have WebKit while Microsoft Windows has IE, Edge, and now Chromium. A fabulous idea in theory but complications like native support for system tray, notifications, file system access, data storage, and security made many hesitate into actually making it a thing.
Released in 2018 by a Sri Lankan programmer, Shalitha Suranga, NeutralinoJS was one of the first frameworks to properly support 3 different desktop platforms while making it extremely easy for web developers. 3 years later, it has its own Javascript client library, support for extensions, and a very minimal footprint (< 3 MB).
While Tauri requires you to install Rust and a boatload of other things, NeutralinoJS stands on the shoulders of giants. It doesn't require learning a new language to take advantage of native capabilities. It lacks the huge npm ecosystem but supports a wide variety of system APIs under the Neutralino
namespace. Currently it supports:
Ease of Use
- Neutralino.init
- Neutralino.app
- Neutralino.computer
- Neutralino.debug
- Neutralino.filesystem
- Neutralino.os
- Neutralino.storage
- Neutralino.window
- Neutralino.events
- Neutralino.extensions
- Neutralino.updater
Installation
Installing NeutralinoJS is as simple as:
npm i -g @neutralino/neu
And a few seconds later, you should have neu
command available globally.
Hello Neutralino
Creating a new NeutralinoJS app is even simpler:
neu create <project-name>
However, most of the times you are either integrating into an existing codebase or you want to use a frontend framework like React. This is where NeutralinoJS shines.
Run the above command in your app's codebase. It should create a directory with the same name. All you have to do after that is edit the neutralino.config.json
file. We'll edit 2 keys: url
& documentRoot
to point them to our framework's build directory. For React, it is:
"documentRoot": "./build/",
"url": "/index.html",
To start the app, enter:
neu run
And your app should open in a native window.
All in all, relative to Tauri, NeutralinoJS is extremely easy to setup and use. Of course, it doesn't have nearly all the features Tauri has but you can easily add those via native extensions.
Security
In terms of security, NeutralinoJS is a disappointment. It hosts the web app on a local server exposed on the localhost — all you have to do to access it is open a browser and go to http://localhost:some-random-port
.
That's not even the worst thing. NeutralinoJS has a Javascript client library with many helpful functions to read files, write files, and access system info etc. This library is exposed on the client side as a global variable Neutralino. If you open the Developer Console in a Neutralino app, you can literally delete & list files using:
const files = await Neutralino.filesystem.readDirectory('.');
for (let file of files) {
await Neutralino.filesystem.removeFile(`./${file.entry}`);
}
And viola! All files in your current working directory are now gone.
Very, very dangerous. I am aware that this is done for Developer experience but if, let's say, you wanted to load untrusted content in a NeutralinoJS app, I'd seriously reconsider. Most apps, however, only load local files for which NeutralinoJS is great. It'd still have been amazing if the client library wasn't exposed as a global variable.
Extensibility
Extensions in NeutralinoJS are simply programs that communicate over Websockets. Naturally, this allows the program to be written in any language or framework. This opens the possibility to write the backend in NodeJS, Rust or Go while the app is rendered in the native browser.
Since it's all Websockets, stuff like converting data structures, translating function arguments, reading results etc. are automatically handled by a lot of Websocket client libraries.
Publising & Updates
NeutralinoJS has no built-in system for receiving or installing updates nor does it have a proper bundler like electron-builder
to make installers for each operating system. All this makes NeutralinoJS nonviable for any but the most trivial projects.
Running the neu build -r
creates a .zip file containing binaries & resources for all 3 platforms. This works but there's no desktop integration, no icons, no way to install etc.
Migrating from Electron
Suffice it to say, there is 0 API compatibility with Electron. There's no node, no npm ecosystem, nothing like that. Your best bet to migrate an Electron app to Neutralino is either rewriting the backend code to a natively compiled language like Go or Rust or you can package the whole NodeJS along as a binary.
Final verdict
Having NeutralinoJS in the Native Javascript space provides necessary competition but it falls short of a lot of things required for a native app (even a native Javascript app) --- especially security. Combine that with this being mostly a hobby project run by a single developer with little financial (or other) support from the community, and you get something that is truly wonderful but not yet there.
Initially, I had plans to migrate Notesnook from Electron to Neutralino because the feature-set of NeutralinoJS is simply amazing. However, the security & reliability provided by Electron is simply unparalleled. If the developer works on bundling & security, NeutralinoJS can become an amazing alternative to Tauri & Electron.