Setting Up a Development Environment in Elm
Tags: elm, functional, parcel
Published: 2020-02-16 11:45:09

As I was building out yet another Elm test project, I wanted to add some CSS to my app. I added a `styles.css` file to my source folder, and soon realized I had nowhere to include it! I've always been running `elm reactor` up until this point, but that doesn't give me access to any styles whatsoever.

After looking in the [docs](https://guide.elm-lang.org/webapps/), I found I could use `elm make` to create a default HTML file for me, but that meant that I had to build my project on every change to iterate on my application. Surely, there's a better approach out there?

Turns out, the options weren't documented very well, but they're definitely out there! This article will show you a few different ways you can set up a development environment for a real world application, complete with hot reloading and all.


## Getting started: Hello again, NPM

The first thing you'll need to do, is to run `npm init` (alternatively `yarn init`) to bootstrap a `package.json` file. I thought I wouldn't have to use a JavaScript package manager for Elm apps, but in order to set up a development environment, you definitely need to.

In any real world application, you'll probably need some JavaScript libraries at some point, too, so don't feel bad about adding it. You'll still be writing Elm most of the time!

## The naïve approach - automation!

The simplest approach I could come up with is to set up a file watcher that runs `elm make --output public/app.js` on every change in my Elm files. I went with [`watch`](https://www.npmjs.com/package/watch), an `npm` package, and added the following to my `package.json`:

```json
{
  "scripts": {
    "start": 
      "watch \"elm make src/Main.elm --output=public/app.js\" src/**/*.elm"
  } 
}
```

Now, I can create an `index.html` file in the `public/` directory, and add my CSS file next to it. When I'm done, my file structure will look something like this:

```
src
 - Main.elm
 - OtherFiles.elm
public
 - index.html
 - styles.css
 - app.js
package.json
elm.json
```

The `index.html` file is super basic, and will look like this:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>My Elm app</title>
    <link href="./styles.css" rel="stylesheet" />
  </head>
  <body>
    <div id="root"></div>
    <script src="app.js"></script>
    <script>
      Elm.Main.init({
        node: document.getElementById("root")
      });
    </script>
  </body>
</html>

```

Now, I can open the `public/` folder with i.e. [`serve`](https://www.npmjs.com/package/serve), and iterate on my app like it ain't no thing.

## The Elm approach: `elm-live`

Even if the above approach technically works, it feels like I've built something with toothpicks and band-aids. Surely, there's something a bit more put together?

Luckily, there is. [`elm-live`](https://github.com/wking-io/elm-live) is the all-Elm alternative that provides us with iterative builds, hot module reloading and a ton of other features. 

To get started, install it with `npm` or `yarn`:

```bash
npm install elm-live
yarn add elm-live
```

Next, we need to add a script to our `package.json` file:

```json
{
  "scripts": {
    "start": 
      "elm-live src/Main.elm --hot --start-page=public/custom.html -- --output=public/app.js"
  } 
}
```

And that's it! Start your script, and a hot-reloading dev server will pop up and instantly improve your developer experience.

## My favorite approach: Parcel

Although the `elm-live` approach is nice and all, we still need to have a bunch of manually edited stuff inside of our `public/` folder. Wouldn't it be nice if some build tool figured all of this out for us?

Meet [Parcel](https://parceljs.org/). Parcel is a bundler for web apps, which happens to support Elm out of the box. In addition, it works with JavaScript, CSS and other resources as well, which will help us a lot down the road. Let's give it a try!

First off, let's install it with your package manager of choice.

```bash
npm install parcel-bundler
yarn add parcel-bundler
```

Next, move your hand-crafted `public/styles.css` and `public/index.html` into your `src` folder. They're source code, after all! We'll create an additional file, `index.js`, which will look like this:

```
import { Elm } from './Main.elm';
import './styles.css';

Elm.Main.init({
  node: document.getElementById('root')
})
```

This lets us remove the inline script tag from our HTML file, as well as the `<link />` CSS tag.

Finally, let's add the script to our start tag:

```json
{
  "scripts": {
    "start": "parcel src/index.html"
  } 
}
```

There's nothing more to it. Run your script, and you'll notice a hot-reloading dev server pop up for you, just like you'd expect. This will even include an Elm debugger tool as well 😎

A nice bonus feature with Parcel, is that you'll be able to add styles processing and JavaScript transpilation in no time at all. That's nice to know!

## That's it!

Turns out, setting up a nice developer experience isn't a lot of work, once you know what to do. I hope this article can help future beginners like myself looking for the best way to get started. 

Please let me know how you set up your dev environment in the comments!