Build your First Electron App

2016-06-09

Getting started tutorial for Electron. In this tutorial you will learn how to:

  • Install Electron
  • Configure the app window
  • Pass arguments from the command line to the UI
  • Use Node libraries in an electron app

You should have some basic understanding of Node and ES6 in order to understand this tutorial.

If you want to dive straight into the code, the github repo is here.

The App

We are going to build a simple timer app that looks like this:

Get Coding

Install Electron

Install electron-prebuilt globally:

1
$ npm install -g electron-prebuilt

Project Structure

Create the following structure and files:

1
2
3
4
5
6
7
electron-timer-app/
├── package.json
├── main.js
└── app/
├── index.html
├── style.css
└── index.js

Package.json

The package.json content will be:

1
2
3
4
5
{
"name" : "electron-timer-app",
"version" : "0.1.0",
"main" : "main.js"
}

main.js is the entry point of the app and will be picked up by electron when running the app.

App UI

I am going to skip the explanations regarding the HTML and CSS as it’s pretty trivial.

Put the following code in your index.html file:

/app/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="style.css" rel="stylesheet" type="text/css"/>
</head>
<body>

<div id="timerDiv" class="timer">12:35</div>

<script type="text/javascript" src="index.js"></script>
</body>
</html>

And use the following css code in style.css:

/app/style.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@import url(https://fonts.googleapis.com/css?family=Orbitron:700);

html, body {
font-family: 'Orbitron', sans-serif;
background: #113A47;
}

.timer {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 75px;
color: #D7ECFF;
text-shadow: 0 0 2px #fff, 0 0 6px #fff, 0 0 10px #fff, 0 0 15px #228DFF, 0 0 25px #228DFF, 0 0 30px #228DFF, 0 0 40px #228DFF, 0 0 50px #228DFF;
}

Open the index.html in your browser to check that its working.

Electron Startup App

The main.js file is the entry point of the app. It will contain all the code needed to start the app. It will contain:

  • Window creation and management
  • Parsing of command line arguments
  • Communicate information to the UI

Here is the boilerplate code needed to get started. Taken from the official electron documentation:

/main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const electron = require('electron');
// Module to control application life.
const {app} = electron;
// Module to create native browser window.
const {BrowserWindow} = electron;

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

function createWindow() {
// Create the browser window.
win = new BrowserWindow({width: 800, height: 600});

// and load the index.html of the app.
win.loadURL(`file://${__dirname}/index.html`);

// Open the DevTools.
win.webContents.openDevTools();

// Emitted when the window is closed.
win.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null;
});
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});

app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});

We are only going to change a few things for now:

App Path:

Because our index.html is in an app folder, we will have to modify line 16

/main.js
1
win.loadURL(`file://${__dirname}/app/index.html`);

Disable Dev Tools:

Comment out line 19

/main.js
1
//win.webContents.openDevTools();

Window size and Appearance:

Replace line 13 with the following

/main.js
1
2
3
4
5
6
win = new BrowserWindow({
width: 500,
height: 300,
frame: false,
resizable: false
});

With this piece of code, we are making our app window frameless, smaller and non resizable. Check out the full list of properties here. It is amazing what you can do.

Start the App

That’s it we are ready to test the app.

In your project root, launch it by typing:

1
$ electron .

A window opens with the timer.

Timer UI Logic

In this part of the tutorial we are going to handle the timer logic (countdown animation).

Because we are in a Node Electron App, we can write our ‘front end’ code using Node JS conventions. Which means that in our /app/index.js it is possible to use ES6 and require Node modules without the need to package our scripts later on.

The timer will be a simple program that removes one second to the initial time every second and prints out the result.

We will use Moment.js for the timer logic. Install it with:

1
$ npm install -save moment

And in our /app/index.js file

/app/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Require moment.js
const moment = require('moment');

// Helper function, to format the time
const secondsToTime = (s) => {
let momentTime = moment.duration(s, 'seconds');
let sec = momentTime.seconds() < 10 ? ('0' + momentTime.seconds()) : momentTime.seconds();
let min = momentTime.minutes() < 10 ? ('0' + momentTime.minutes()) : momentTime.minutes();

return `${min}:${sec}`;
};

// Initial time, hardcoded
let currentTime = 255;

// Print out the time
timerDiv.innerHTML = secondsToTime(currentTime);

// Execute every second
let timer = setInterval(() => {

// Remove one second
currentTime = currentTime - 1;

// Print out the time
timerDiv.innerHTML = secondsToTime(currentTime);

// When reaching 0. Stop.
if(currentTime <= 0) {
clearInterval(timer);
}
}, 1000); // 1 second

Run the app with electron . and look at the countdown animation.

Passing the initial Value in the command line

What would be great now is to pass the initial seconds when launching the app, i.e electron . 255 for a 255 seconds timer.

Let’s go and create that.

Get the time value in main.js

We need to capture the time argument in the node app. This is done with the following code:

main.js
1
2
3
// Capture command line argument
let argsCmd = process.argv.slice(2);
let timerTime = parseInt(argsCmd[0]);

Send the value to the UI

The value is sent to the UI using the webContents property of the BrowserWindow Object.

When the HTML and JS app have finished loading, we want to send the value to the UI:

This is done with the following code:

main.js
1
2
3
4
5
// When UI has finish loading
win.webContents.on('did-finish-load', () => {
// Send the timer value
win.webContents.send('timer-change', timerTime);
});

Check out the full main.js code here.

Retrieve the value in the app’s index.js

To retrieve the value, we need to use ipc-renderer.
This module is a simple Event Emitter that can send and listen to events (also known as channels in the doc). Use the on() method to listen to the 'timer-change' event sent by the win.webContents above.

/app/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Require moment.js
const moment = require('moment');
// Require ipcRender
const {ipcRenderer} = require('electron');

// [...]

// Listen to the 'timer-change' event
ipcRenderer.on('timer-change', (event, t) => {
// Initialize time with value send with event
let currentTime = t;

// Display and timer logic
// [...]
});

You can see the full index.js here.

That is it ! Start your app with for example

1
$ electron . 74

and observe the result.

I hope you have learnt something useful from this tutorial, it goes a bit beyond the basics by looking at how to use node libs in the client code and at how to communicate between the core and the UI.