Sprinkle some Elm in your React
Tags: elm, react
Published: 2020-10-04 13:37:18

---
title: Sprinkle some Elm in your React
published: true
date: 2020-10-04 11:01:58 UTC
tags: Elm,React
canonical_url: https://andreligne.io/sprinkle-some-elm-in-your-react/
---

 ![Sprinkle some Elm in your React](https://images.unsplash.com/photo-1506224772180-d75b3efbe9be?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

After being out of the [Elm](https://elm-lang.org) world for about a year, I decided this weekend to try it out again. I created a new application (using [create-elm-app](https://github.com/halfzebra/create-elm-app)) and got going. After a few minutes, the joy of writing Elm was back. The helpful error messages, the flexibility of describing data in the type system - it was fantastic!

In another side-project I've been working on for a few months, we are using React. Even though React is an excellent framework for building web applications, my fingers started to itch for writing Elm again.

Then it occurred to me - why can't we write an Elm module for a small set of a screen and embed that in a React component?

So that's what this post is about!

* * *

To get started, we need a project set up using `webpack` capable of compiling both React and Elm files. I'll leave this as an exercise for the reader.

This is the contents of the `Main.elm` file that we'll use to try and figure this one out.

```
module Main exposing (..)

import Browser
import Html exposing (Html, text)

main : Program () Model Msg
main =
    Browser.element
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }

type alias Model =
    {}

init : () -> ( Model, Cmd Msg )
init flags =
    ( Model
    , Cmd.none
    )

type Msg
    = Noop

update : Msg -> Model -> ( Model, Cmd Msg )
update _ model =
    ( model, Cmd.none )

view : Model -> Html Msg
view model =
    Html.div [] [text "Hello from Elm!"]

subscriptions : Model -> Sub Msg
subscriptions _ =
    Sub.none
```

This doesn't do anything interesting since it only prints out the string `"Hello from Elm!"`, but it's good enough to try and get it running inside of a React component.

![Sprinkle some Elm in your React](https://andreligne.io/content/images/2020/09/Screenshot-2020-09-30-at-21.53.56.png)<figcaption>Screenshot of the Elm application we'll wrap</figcaption>

## How does an Elm application initialize?

When you create any Single Page Application (SPA), you usually have some entry point Javascript that imports the SPA's main module and mounts it on a specific DOM node on the page.

To take a closer look at this, we can open the generated `index.html` file.

```
import { Elm } from '../Main';

document.addEventListener('DOMContentLoaded', () => {
  const target = document.createElement('div');
  document.body.appendChild(target);
    
  Elm.Main.init({ node: target });
});
```
<figcaption>Example <code>hello_elm.js</code> from a new project</figcaption>
- First, we import the Elm application from the `Main.elm` file
- On the `DOMContentLoaded` event, we create a new DOM element for a `div` and append it to the body
- We then initialize the Elm application to run inside the new  `div`

The main takeaway here is that we will need a DOM node to run our Elm application inside.

## Creating the React component

Now that we understand how the Elm application gets initialized, we can start thinking about designing our React component to host it.

Since the only thing we need to initialize the Elm application is a DOM node, let's create a component that renders a `div` we can use as our target.

```
import React from 'react';

export default function ElmComponent() {
  return <div />;
}
```

To get the reference of that `div`, we can use the `useRef` hook from `react` to get a reference to the DOM node for the entire lifecycle of the component. The `useRef` function takes an initial value

```
import React, { useRef } from 'react';

export default function ElmComponent() {
  const target = useRef();

  return <div ref={target} />;
}
```

Now that we know where to put the application, we can import our Elm module and use the `useEffect` hook to initialize it when the component first mounts to the DOM.

```
import React, { useRef } from 'react';
import { Elm } from '../Main';

export default function ElmComponent() {
  const target = useRef();

  useEffect(() => Elm.Main.init({ node: target.current });

  return (
    <>
      <h1>Hello from React!</h1>
      <div ref={target} />
    <>
  );
}
```

Now when we render our `ElmComponent`, our Elm application will run _inside_ our React application.

![Sprinkle some Elm in your React](https://andreligne.io/content/images/2020/09/Screenshot-2020-09-30-at-21.53.20.png)<figcaption>The final result</figcaption>

## How can we pass data from React?

While embedding the Elm application in an already existing React application, there's a good chance you want to send along some data from the React app to the Elm app. This could be anything from authentication tokens for making HTTP requests to a server to the current user.

To do this, we can change the type signature of our `main` function in the Elm program to signal that we're expecting the program to receive initial data when it starts up. We also then to add to the data we want to hold in the program to our `Model` type.

For passing an authentication token from React into the Elm program, so that we can use it to make HTTP requests, we could change our module to something like this.

```
import Browser
import Html exposing (Html, text)

main : Program String Model Msg
main =
    Browser.element
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }

type alias Model =
    { authToken : String }

init : String -> ( Model, Cmd Msg )
init authToken =
    ( Model authToken
    , Cmd.none
    )

type Msg
    = Noop

update : Msg -> Model -> ( Model, Cmd Msg )
update _ model =
    ( model, Cmd.none )

view : Model -> Html Msg
view model =
    Html.div [] [text <| "Token: " ++ model.authToken]

subscriptions : Model -> Sub Msg
subscriptions _ =
    Sub.none

```

The differences in our new Elm program is:

- `Model` is constructed with an  `authToken : String` value
- The type signature of `main` now specifies the type of the `flags` passed to it
- `init` also has an updated type signature an build the initial `Model` with the value from the flags

If we now render the `ElmComponent` and pass along a string as the `flags` prop, the Elm program will store that inside the initial model so that we can use it later. Let's run the new program.

```
import React, { useRef } from 'react';
import { Elm } from '../Main';

export default function ElmComponent() {
  const target = useRef();

  useEffect(() => Elm.Main.init({
    node: target.current,
    flags: "my-very-secret-token"
  });

  return <div ref={target} />;
}
```

![Sprinkle some Elm in your React](https://andreligne.io/content/images/2020/10/Screenshot-2020-10-02-at-11.30.27.png)<figcaption>Our rendered Elm program with arguments passed from React.</figcaption>

You can also pass along more complex data to your Elm program using `flags`, such as objects, tuples, arrays and so on. The Elm guide has great documentation if that is something you want to read more about!

[Flags · An Introduction to Elm](https://guide.elm-lang.org/interop/flags.html)

## In summary

By using this little building block in your React application, you can start introducing Elm into your codebase without rewriting the application.

This can help you try if you like it enough in a low-risk situation and later decide if you want to expand your usage of it throughout the application. Otherwise, you only have a small portion of Elm running and can easily convert it back to React.

But since Elm is fantastic, that'll probably never happen. 😁