When I build a project I like to use Elm for my frontend and Erlang for my server side stuff. Some people prefer Elixir, but I started with Erlang before Elixir was around, so I will stick with it. That being said if you want to build web stuff on the BEAM (Erlang VM) using Elixir and Pheonix is probably a great idea.

Where to put the Elm files

I assume that you are building Erlang with Rebar3 and shipping the code with a release. (If you are not why not?). SO the first question you should ask is where to put the elm files? I like to put the Elm files in an elm_src dir and have the compiled .js and .css files go in priv. That way you can access them with the standard Erlang application structure.

If you want to have the Elm code in its own Erlang application, then you might want to have a structure that looks something like this. The only Erlang file that really does anything is elm.erl which will have some functions to return the content of the .js and .css files.

rebar.config
elm-package.config
Makefile
priv/elm.js
priv/elm.css
src/elm.app.src
src/elm_app.erl
src/elm.erl
elm_src/Main.elm
elm_src/<<OTHER FILES>>.elm

Including Elm In an Erlang binary

If you are expecting some load in production, you might find that having erlang load the elm content from the disk each time to be a bottleneck. In this case check out Ulf Wiger’s Parse Transform package.

Take a look at this example. There are two functions elm/0 and elm_live/0. The elm_live/0 function will load the elm code and return {ok, Code} where the code is binary that contains all the javascript. The key point is that assuming that the elm.js file has not changed on disk since the erlang code was compiled these two things should return the same content. However the elm_live/0 function will take much longer return.


-compile({parse_transform, ct_expand}).
-pt_pp_src(true).

-spec(elm() -> {ok, binary()}).
elm() ->
    ct_expand:term(elm_live()).

-spec(elm_live() -> {ok, binary()}).
elm_live() ->
    PrivDir    = code:priv_dir('elm'),
    Filename   = filename:join([PrivDir, "elm.js"]),
    {ok, Data} = file:read_file(Filename),
    {ok, Data}.

Just make sure that your build system builds the elm code BEFORE you build your erlang code.

Building Elm with Rebar

First of all I would say that I don’t do this, I find that it is less work and more predictable to just use a Makefile. But it is possible. My experience was that it caused more problems than it solved. As it meant that it was impossible to compile, test or run the Erlang code without also running Elm.

Rendering HTML

Of course in order to run Elm code you need to have some HTML to embed it in. I found that the best way to do this is to just use erlydtl Which gives you Django template syntax. I like to take any data I want to preload into the system and insert it into the template in Javascript (JSON) format and then use it as init data for my elm code. In development I like to pretty print the data, in production its probably not the worth it.

Web Servers

There are a few main options for building Web resources in Erlang. Cowboy seems to be the favorite these days and has a lot of momentum behind it. I like to use Webmachine, It is a great system and is very solid. I will do another blog post about it later. You could also use Yaws.

What ever you use make sure you setup etags correctly so that people don’t reload the compiled elm code more often then they have to, you should also enable content negotiation so that the file is sent in gzip format.

Use a CDN

when you are done with your Elm code you get a few static files out of it. Your users will get faster loads if you put it on a CDN. You can find a CDN for small projects that will be free or cost a few dollars a month. It should take you all of 5 minutes to configure.