So now we can set Elm up in any <div> we want. So if you are using React, you can create a component that just sets this kind of thing up. If you are using Angular or Ember or something else, it should not be too crazy either. Just take over a <div>.

The next section gets into how to get your Elm and JavaScript code to communicate with each other in a nice way.

Step 2: Talk to JavaScript

There are two major ways for Elm and JavaScript to talk to each other: ports and flags.

Ports

Say we have a nice Elm program and everything is going fine, but we want to use some JavaScript spell-checking library to get a feature done real quick. The final result is shown here, and we will walk through the most important parts here.

Okay, in Elm, any communication with JavaScript goes through a port. Think of it like a hole in the side of your Elm program where you can send values in and out. These work exactly like the commands and subscriptions from the Architecture section. Sending values out to JS is a command. Listening for values coming in from JS is a subscription. Pretty neat!

So if we want to talk to this spell-checking library, our Elm program will need these additional declarations:

Again, you can see the whole file here, but these are the important additions:

We change the module declaration to port module. This indicates that port declarations should be permitted. (Very few modules should have ports in them!)

We create a check port. On the Elm side, we can create commands like check "badger", resulting in a Cmd msg that sends strings to the JS side.

We create a suggestions port. This one looks a bit fancier than the check port, but imagine that it is creating Sub (List String). You are essentially subscribing to lists of strings sent into Elm from JS. So when the spell-checking library has a suggestion, it will send things through. Now, the type of suggestions is a bit fancier than that. You provide a function from (List String -> msg) so you can convert that list of strings to your Msg type immediately. This makes it easy to deal with in your update function, but it is just for convenience. The real point is to send a List String from JS into Elm.

Okay, so after you run elm-make Spelling.elm --output=spelling.js you embed it in HTML like this:

Okay, so all the ports you declare in your Elm program will be available as fields of app.ports. In the code above, we access app.ports.check and app.ports.suggestions. They work like this:

We can subscribe to app.ports.check. Every time Elm says to send a value out, we will call this JavaScript function.

We can send values to app.ports.suggestions. So whenever we have some new suggestions for Elm, we just send them through.

With that knowledge, we can communicate back and forth with JavaScript!

Note: Elm validates all values coming in from JavaScript. In Elm we said we can only handle List String so we need to make sure that the JavaScript code does not break that contract! More about that farther down this page.

Flags

The second way to talk to JavaScript is with flags. You can think of this as some static configuration for your Elm program.

Instead of creating a Program with the program function, we can use the programWithFlags. So say we want to get a value like this from JavaScript on initialization:

Notice that this is exactly the same as normal, but we provide an extra argument with all the flags we want.

Just like ports, the values sent in from JavaScript are validated to make sure JavaScript bugs stay in JavaScript.

Customs and Border Protection

Ports and flags must be careful about what values are allowed through. Elm is statically typed, so each port is fitted with some border protection code that ensures that type errors are kept out. Ports also do some conversions so that you get nice colloquial data structures in both Elm and JS.

The particular types that can be sent in and out of ports are quite flexible, covering all valid JSON values. Specifically, incoming ports can handle all the following Elm types:

Now say Elm wants a List String, but someone calls app.ports.suggestions.send(42) on the JavaScript side. We know it will cause issues in Elm, and we know the code producing invalid data is on the JS side. So rather than letting the bad data into Elm and cause a runtime exception eventually (the JavaScript way!) we throw a runtime exception immediately when you call send with invalid data. So we cannot solve the problem of invalid data in JavaScript, but we can at least make sure it stays on the JavaScript side!

Usage Advice

I showed an example where the ports were declared in the root module. This is not a strict requirement. You can actually create a port module that gets imported by various parts of your app.

It seems like it is probably best to just have one port module for your project so it is easier to figure out the API on the JavaScript side. I plan to improve tooling such that you can just ask though.

Note: Port modules are not permitted in the package repository. Imagine you download an Elm package and it just doesn't work. You read the docs and discover you also need to get some JS code and hook it up properly. Lame. Bad experience. Now imagine if you had this risk with every package out there. It just would feel crappy, so we do not allow that.

Historical Context

Now I know that this is not the typical interpretation of language interop. Usually languages just go for full backwards compatibility. So C code can be used anywhere in C++ code. You can replace C/C++ with Java/Scala or JavaScript/TypeScript. This is the easiest solution, but it forces you to make quite extreme sacrifices in the new language. All the problems of the old language now exist in the new one too. Hopefully less though.

Elm's interop is built on the observation that by enforcing some architectural rules, you can make full use of the old language without making sacrifices in the new one. This means we can keep making guarantees like "you will not see runtime errors in Elm" even as you start introducing whatever crazy JavaScript code you need.

So what are these architectural rules? Turns out it is just The Elm Architecture. Instead of embedding arbitrary JS code right in the middle of Elm, we use commands and subscriptions to send messages to external JavaScript code. So just like how the WebSocket library insulates you from all the crazy failures that might happen with web sockets, port modules insulate you from all the crazy failures that might happen in JavaScript. It is like JavaScript-as-a-Service.