tl;dr use ports and localstorage

Why

Some reasons why you may want to communicate between browser tabs:

Keep data from an API in sync/cached Login a user in all open tabs when they login in one Store auth tokens Use one tab to control the UI in another

In the rest of this post, I will show you a super simple example of how to talk between tabs in Elm using ports and localstorage.

Example

Click the buttons to change the counter value - then open this page in another tab, you will see the counter update in both places.

How it works

Every time we change our model in Elm, we send it out a port in our update function.

update : Msg -> Model -> ( Model , Cmd Msg ) update msg model = let newModel = updateHelper msg model in ( newModel , saveToStorage newModel ) port saveToStorage : Int -> Cmd msg

On the JS side, this port saves the model to localstorage.

app . ports . saveToStorage . subscribe ( function ( m ) { // save our model to local storage localStorage . setItem ( lsKey , JSON . stringify ( m )); });

Still in JS land, we have an event listener that subsribes to localstorage: if our model is updated in localstorage, then we send it back into a port in our Elm app.

window . addEventListener ( 'storage' , function ( event ) { if ( event . key === lsKey ) { // if the model changes, pass it into elm app . ports . fromStorage . send ( event . newValue ); } });

In elm, we then decode the value and put it in our model.

port fromStorage : ( Maybe String -> msg ) -> Sub msg subscriptions model = fromStorage FromStorage update : Msg -> Model -> ( Model , Cmd Msg ) update msg model = let newModel = updateHelper msg model in ( newModel , saveToStorage newModel ) updateHelper : Msg -> Model -> Model updateHelper msg model = case msg of Increment -> model + 1 Decrement -> model - 1 FromStorage ms -> decodeLocalStorage ms decodeLocalStorage : Maybe String -> Int decodeLocalStorage ms = case ms of Nothing -> 0 Just s -> Json . Decode . decodeString Json . Decode . int s |> Result . withDefault 0

You can see the full source code in this gist. If you want to see a more complicated example, have a look at apostello.