Learning Electron: 4 Simple Things I Wish I Knew Sooner

Electron is a fun stack to work with. If you’re familiar with Node or with building front ends in the browser, Electron makes it easy to build desktop apps.

Although the community surrounding Electron makes it easy to get started, there are a lot of mistakes people frequently make and things people misunderstand that chew up a lot of their time.

For the sake of brevity here, I’ll assume that you’ve gone/will go through the Electron getting started docs. Here are 4 things that I personally have learned. I hope they can help you.

1. Keep your Code Separated

In Electron, the front-end windows and the main process all run JavaScript, so it can be tempting to keep all of the code together. Some code will not play nicely when it’s run in the wrong area. For example, if you run code in the main process which tries to manipulate something in the traditional browser’s window object, your application will not fare so well.
“I’ll just be careful,” you might say; developers are after all, masters of hubris above all else. It’s well worth it to spend 20 minutes up front avoiding hours of refactoring later. I generally find it worthwhile to separate code into a few folders: one for the main process, one for each type of window, and one for shared code that should get run across all types of processes. It looks like this:

background/ — a hidden window (more on these later)

main/ — main process

renderer/ — the window the user will interact with

shared/ — code that gets loaded for both the main process and all windows

2. Accommodate for Native Modules

I won’t go into the details about native modules here, but the gist of it is that they are C/C++ code that you can call in node as if they’re plain JavaScript modules. Like any C/C++ code, they have to be compiled. npm generally handles this for us (you probably have used plenty of native modules without realizing it), but because Electron has modified Node, they have to be compiled differently.

The prevalent workflow for this is to install normally, and then run a Grunt task or similar script to compile your modules again to work with Electron. My best guess is that people prefer compiling twice so that they have more time for swordfighting. However, while I enjoy swordfighting, I also have entirely too much to get done, so I prefer to only compile code once.

To add to it, while most modules will compile in 30 seconds or so, some larger modules like NodeGit can take nearly 10 minutes to compile, so compiling twice is a brutal time waster. Mentioned very briefly in the Electron docs is an alternative, which is the way I prefer to do things. Code is only compiled once, plus this way we can take advantage of modules that provide pre-compiled binaries. Using a file called .npmrc in the root of our project, we can get npm to compile/download everything correctly from the get go.

The other thing to pay attention to here is that native modules compiled for Electron won’t necessarily work normally in Node. so If you have a Grunt build process or anything of the sort which uses some native modules, they’ll need to be kept separately.

What I do (and what they do in Atom) is to separate out modules into two areas to distinguish between those used in the application, and any modules used in the build but outside of the actual application. I keep those used in the application (including actual dependencies, and dev dependencies like test frameworks that run inside of the application) in the root of my project, and a build folder with its own package.json file for those modules used outside the application:

3. Don’t Block the UI Thread OR the Main Thread

Node is single-threaded. While Electron runs multiple processes, each one is still single-threaded. So, if you run a long running synchronous task in your window, the user isn’t going to be able to interact with your application for the duration of its task. Unless you’re out to spite the end user, this is generally regarded as “bad.”

The common first attempt at getting around this that new-to-Electron developers make is to move things to the main process, but this is still “bad.” Lots of small actions (like quitting the app, resizing the window, etc.) happen in the main process, so now, while your buttons are still clickable, the app can’t resize, or exit, or any number of other related things which make the app seem frozen and broken to the end user.

In the browser, people generally get around this by using Web Workers, but rather than spend time implementing that, the Electron team decided to make it even easier. To perform processes that don’t block the main process or renderer process, all you have to do is open another window that isn’t visible to the user.

This invisible window will open up the other process behind the scenes. Despite being invisible to the user, it can communicate with the main process (and other windows) using Electron’s IPC module just like a normal viewable window.

var backgroundWindow = new BrowserWindow({show: false})

4. Use Old School for Web Requests

If your app needs to do any communication over the Web (perhaps to an API server or such), it’s logical to use the built-in node http/https modules, or the popular request module. However, none of these accommodate for system-configured proxies.

Chromium, however, does account for this, and fortunately, it’s super easy to tap into that, just by using the old-school browser XMLHttpRequest object. By using XMLHttpRequest, it’s all handled automatically, and you don’t have to do any extra work or make the user configure anything in your application.

Take Advantage of my Suffering

I hope you’ll take a real stab at using Electron. I spent plenty of my supposed vacation time on the beach still working, but maybe these tips mean that next time you go on vacation you can actually do some vacationing.