ports is one of the main ways to interact with . However I feel like I need to tell you that if you can get away with it, you should just use flags. Flags pass in some initial value to Elm to start with and is the easiest way no doubt. However if you want more back and forth communication ports is the way. Elm JavaScript High-Level Overview First, here’s the picture: Ports are basically the pub-sub pattern (also called The Observer Pattern). . A port is the connecting pipe between Elm and JavaScript Each port is only one direction. Either Elm ->JavaScript or JavaScript -> Elm. So naturally if you want two way communication you just have to make two. Making a Port Some Setup If you’re going to follow along follow these steps (or perhaps ) just look at the code Setup an file with the full Step 1: Main.elm Html.program A file that can be blank for now Step 2: .js An file that includes both (compiled elm file) and the file that contains the code you want to interact with. Step 3: index.html elm.js whatever.js From Elm to JavaScript Annotate the module by putting a at the beginning port Create a port JavaScript with a type signature like to port : String -> Cmd msg toJs This will called that creates s (commands). If you remember you can return as the second part of the tuple in the function! Let’s do that. creates a function toJs Cmd Cmd update update msg model =case msg of SendToJs str -> ( model, **toJs** _str_ ) This will magically send to JavaScript land! Well only to those who subscribe to it. To subscribe to it you do: _str_ var node = document.getElementById('view');var app = Elm.Main.embed(node); // receive something from Elm (function ( ) {console.log("got from Elm:", );}); app.ports.toJs.subscribe str str From JavaScript to Elm In JS land you something via the method on the port. Like so: send send var node = document.getElementById('view');var app = Elm.Main.embed(node);app.ports. ("undefined is not a function"); toElm.send Naturally JS says it’s customary phrase. So we define a new port: port toElm : (String -> msg) -> Sub msg This creates a function, that can turn strings JS sends and turns it into a (again can be any value that’s compatible between the two) This one returns a instead of a so we need to to in Elm: that takes a function Msg Sub Cmd subscribe subscriptions : Model -> Sub Msgsubscriptions model =toElm UpdateStr Basically this says: whenever JS sends me something over the _toElm_ port send the _UpdateStr_ message to the _update_ function. We can handle this just like any other in our update function: Msg update msg model =case msg ofUpdateStr str ->( { model | message = str }, Cmd.none ) Basically I’m just storing the string JS gave me to a part of my model. WARNING: Run-time Exceptions Ahead! 🚨 🚨 While I’ve used to keep this simple but this is . String dangerous If JavaScript, (in it’s infinite wisdom), decides to send something that’s (like null, undefined, NaN, rubber duck, a function, etc.) then . not a string Elm will throw a run-time exception and stop it’s run loop This means Elm will not be able to accept any input or respond in any way. It’s dead. The way to make your elm code to change the type of the port to a instead. This means that you’ll have to write . This way if JS decides to send you a bad value it will just return an instead of throwing an exception. bullet proof again Value a Json.Decoder for it Err See the diff here import Json.Encode exposing (Value)import Json.Decode as Decode -- Farther down in the file.... -- port toElm : ( -> msg) -> Sub msg Value -- SUBSCRIPTIONS subscriptions : Model -> Sub Msgsubscriptions model =toElm ( ) decodeValue decodeValue : Value -> MsgdecodeValue x =letresult = incase result ofOk string ->UpdateStr string Decode.decodeValue Decode.string x Err \_ -> UpdateStr "Silly JavaScript, you can't kill me!" That’s it! Here’s the picture again: Here’s a link to the working repo: https://github.com/justgage/complete-elm-port-example