Building a cross-platform desktop app with Electron and Elm

Zach Koch
4 min readFeb 14, 2016

--

Updated for Elm 0.17.1: Thanks to Tony Onodi and others for calling out the necessary changes. And if you have trouble in Windows check out Lourens Rolograaf’s comment below.

I’m going to walk you through the steps of setting up an Electron App that uses Elm. If at any point you have trouble, let me know.

By the End of this tutorial you should have a desktop application running on your system displaying a (very basic) Elm UI.

The code for this tutorial can be found here as well as in the gists below.

We will:

  1. How it all works
  2. Install Node (Skip to this section to start getting your hands dirty!)
  3. Get Elm and Electron
  4. Install Elm dependencies
  5. Create the HTML
  6. Create ports.js
  7. Create the Elm file
  8. Create main.js
  9. Automating your Build

How it all works

Electron is Basically a Chrome browser that will display your app, with a couple big differences.

  1. It will run your app locally (no internet required)
  2. Your app will have access to system files and options. You can do things like create and delete files and make system tray widgets.

We’ll need the following files to run the app.

main.js: This file will be run by electron-prebuilt. It tells electron which files to display and gives us options for accessing OS goodies like the system tray.

Main.Elm: This file is where we will build our UI. We will then compile it to regular javascript using elm-make.

index.html: This is the page that Electron will display and it is where we will insert our compiled elm file and ports.js

ports.js: This file will start the elm app and get a reference to it which will allow us to communicate with the UI in the next tutorial.

Here’s a lovely diagram of the process.

Install Node

I’m going to assume you already have Node installed, if not get NVM and if you can’t figure it out or you use Windows (or you’re lazy), get a free tier account at Nitrous.IO. No affiliation, they’re just awesome.

Get Elm and Electron

First you’ll hop into your trusty terminal and:

Create a new directory (call it whatever you want, mine will be called feldspar)

mkdir feldspar && cd $_

Now Initialize the project

npm init

You can just press <enter> through the defaults. Once you’ve done that a package.json file. We’ll come back to this file later when setting up scripts.

Now we’ll install Electron and Elm globally which lets us use them in the terminal.

npm i -g elm electron-prebuilt

Install Elm dependencies

We need to get an elm package to build our UI.

(If you mess something up, delete the elm-stuff folder and do it again.)

elm package install elm-lang/html

This package lets us create html elements in our elm code.

This package will also install the core dependencies needed to compile elm

Create the Html

First let’s make the html that will serve as the container for our elm app.

Create the file index.html and include the following.

Create the Elm file

Now we’ll make a very simple “hello world” in Elm

Create a file called Main.elm with the following:

Simple right? We just import the Html package and use it to make some text which is displayed by main.

Now to compile the elm file we just run this command.

elm make Main.elm --output elm.js

Create ports.js

We need to make a file that will start our elm app and later communicate with it. Here is the ports.js file you will need.

Since we already added elm.js and ports.js to our html when you open index.html in your browser, you should see “hello world”.

Create main.js

Now that we have a working elm app let’s put it on the desktop. To do that you need a main.js file for electron to run. I just happen to have one for you right here!

This will create a 1024 x 768 window, load the elm app, and handle the basic window management.

To start your app, just run

electron main.js

You should get a window with your hello world!

We did it, Elm in desktop app.

Pat yourself on the back, you are a buzz word superstar!

Bonus: Automate your build

For those who don’t want to have to run commands every time I’ll show you a simple way to automate your build.

We need an app that will watch specific files for changes. I like chokidar

npm i -g chokidar-cli

I use chokidar like this:

chokidar 'files to watch' -c 'command to run when they change'

To watch an elm file for changes and compile you can run:

chokidar '**/*.elm' -c 'elm make Main.elm --output elm.js'

The ** part tells chokidar to look in the current directory and all subdirectories.

For convenience I’ve added the elm and electron commands to my package.json file in the scripts section.

The || true at the end of the elm script tells npm to hide it’s own warnings when the compiler fails. This way we only see the compiler’s output.

elm:watch recompiles Main.elm when there are changes to any elm file

The & in-between the script names in the watch script tell npm to run these in parallel.

The commands in the scripts section can be run like so:

npm run <name of the script>

So to compile my elm I run:

npm run elm

And to start watching elm for changes:

npm run elm:watch

The script called watch, starts the app and starts watching elm.

Now we need to set up reloading in electron.

First install a local version of chokidar for main.js to use

npm i -D chokidar

Then add it to main.js (lines 3 and 14):

Now the window will reload when ports.js, index.html, or elm.js are changed!

Feedback

If you got this far in the article you’re probably awesome, so let me know what you think in the comments.

--

--