Intro to Electron.js - Part 1: Setup

Joshua Hall

In this article we’ll be learning how to develop our own native desktop applications using Electron.js. We’ll be making a basic todo list app to master the fundamentals behind creating menus, working with local data, and bundling our apps into something we can run on our local machine. If you ever wish to explore any of these options deeper, I would take a look at the official docs.

Prerequisites

Only some Node.js basics are necessary, but since we’ll be using Nedb, which is built on MongoDB and Mongoose, being familiar with using a NoSQL database will be very helpful, which you can learn more about here.

Installation

We only need a few things to get things started.

  • electron
  • electromon A version of nodemon for live reloading electron apps.
  • nedb A version of MongoDB and Mongoose that allows us to save data directly to our machine.
  • electron-packager A builder for our app so we can send and download it later.
$ npm i electron electromon nedb electron-packager

File Structure

All we really need to get started is a place to put our icons for our different operating systems, a HTML page with a client-side JavaScript file, and a file for our server. We’ll also break our Menu Bar into it’s own component since it can get a bit cumbersome.

* assets 📂
  * icons 📂 // electron-packager needs a different logo format for every platform
    * mac  // mac and linux, since they take the same file type
    * win 
* src 📂 
  * MenuBar.js
  * index.html 
  * script.js
* app.js
* package.json

Setup

For our project’s package.json file, the scripts object may seem complicated, but we’re just adding a start script and some build commands for when we’re ready to deploy to various platforms. In the build commands, make sure the --icon path leads to the correct location and ProductName equals the name of your app, we’ll worry about the icons later.

package.json

{
  "dependencies": {
    "electromon": "^1.0.10",
    "electron": "^5.0.8",
    "electron-packager": "^14.0.3",
    "nedb": "^1.8.0"
  },
  "name": "electron-app",
  "version": "1.0.0",
  "main": "app.js",
  "devDependencies": {},
  "scripts": {
    "start": "electromon .",
    "package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --icon=assets/icons/mac/icon.icns --prune=true --out=release-builds",
    "package-win": "electron-packager . --overwrite --asar=true --platform=win32 --arch=ia32 --icon=assets/icons/win/icon.ico --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"Electron App\"",
    "package-linux": "electron-packager . --overwrite --asar=true --platform=linux --arch=x64 --icon=assets/icons/mac/icon.icns --prune=true --out=release-builds"
  },
  "author": "",
  "license": "ISC",
  "description": ""
}

Let’s get our interface set up with just a basic HTML page. I will be using the Tailwind CSS framework to make it look a little nicer. All we really need is a form to enter our new items, and and empty ul to append them onto later.

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
  <title>Electron</title>
</head>

<body class="bg-gray-900 text-white flex flex-col items-center">
  <form>
    <input type="text" placeholder="New Item" class="bg-transparent rounded h-12 mt-4 pl-3">
    <button type="submit" class="bg-blue-800 rounded h-12 w-16">Add</button>
  </form>

  <ul></ul>

  <script src="./script.js"></script>
</body>
</html>

To avoid getting an error, we’re just going to set our menu bar to an empty array, and it’ll be passed to and rendered by app.js. We’ll go into more detail on that later.

MenuBar.js

const menuBar = [];

module.exports = menuBar;

Window

After requiring everything, we’re going to destructure all of the tools we need off of electron.

  • app Controls the lifecycle of our app; whether it’s on, shutting off, reloading, etc.
  • BrowserWindow Establishes the window itself, it comes with a long list of options but all we really need is to tell it to use Node.js on our client side.
  • Menu Sets our menu bar from our template over in MenuBar.js
  • ipcMain Controls event calls between the server and the client. It’s client-side counterpart, ipcRenderer, sends our data to our server while ipcMain will catch that before we can save the data to our machine.

Since we are essentially just making a web page we can just pass our new window our page URL, which we can make a bit more palatable using url.format().

app.js

const electron = require('electron');
const url = require('url');
const path = require('path');

// Components
const menuBar = require('./src/components/MenuBar');

const { app, BrowserWindow, Menu, ipcMain } = electron;

let mainWindow;

app.on('ready', () => {
  mainWindow = new BrowserWindow({
    webPreferences: { nodeIntegration: true } // This will allow us to use node specific tools like require in our client side javascript.
  });
  mainWindow.loadURL(url.format({
    // All this is doing is passing in to file://your_directory/src/index.html
    pathname: path.join(__dirname, 'src/index.html'),
    protocol: 'file:',
    slashes: true
  }));

  // Shut down app when window closes
  mainWindow.on('closed', () => app.quit()); 

  const mainMenu = Menu.buildFromTemplate(menuBar);
  Menu.setApplicationMenu(mainMenu);
});

Conclusion

Since this article started to get excessively long, I decided to break it into two parts to make it a bit more graspable. What we have so far makes a good boilerplate and In part 2 we’ll be going over the more interesting bits like communicating between the client and the server, managing our database, and bundling our app. You can find the repo for the completed example here.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#