Gradual migration from React SPA

Hi, folks.

I’ve been following Elm from somewhat of a distance for a while, but I’m starting to put some effort into a gradual migration proposal for using Elm at work. We’re currently using React + ReactRouter to create a single page app. I understand it’s very easy to write a piece of the UI in Elm and then render it as a React component. But I’m trying to figure out where one goes from there.

As the Elm code takes over more surface area from React, it’s going to need to start handling routes on its own. But I can’t tell if that’s possible without upgrading to a Browser.application. My first thought was that I could have an Elm Browser.application and my ReactRouter code and let them handle different routes. I’d render the Elm app in a different div and use classes to hide the UI that is inactive. I quickly discovered, though, that Browser.application takes over the entire body, not just the DOM node provided, so this is a no go.

Has anyone successfully done a gradual migration from something like ReactRouter to an Elm SPA?

One constraint I didn’t clarify is that I can’t load a new page. The app does client-side encryption and stores keys in memory, not in browser storage, so to keep the session alive it has to be a full SPA.

That said, I just thought of something that might possibly work: loading the Elm app in an iframe that takes over the page. Then Elm can control the entire body like it wants to and the React host app can send secrets (very carefully) with postMessage and hide the Elm frame when necessary. The only problem is routing. It’d be tricky, but it might be possible to sync that up between the frame and its host.

I’m guessing my constraint of one SPA is the reason I can’t find much on this. For most situations it makes sense that you’d just load a new page with the Elm app and it would take over.

We did this. We started by creating some components in Elm and loading them inside React. For a while, Elm will just send message to JS via ports for doing routing. Trying to mix the Browser application router and React router doesn’t work well. One of these needs to be the one doing the routing.

At some point we had enough Elm that we decided to swap the foundation of the app (the root of the app and the router), but we didn’t want to re-write the stuff that was already working in React.

We had to swap the core of the app in one big PR. This took me a few days to re-write in Elm. We kept all the React component we already had by loading them using custom elements. This works really well, until now we still load a bunch of React this way.


My main concern is how much code needs to be loaded. With React doing the routing, each route will need to be its own Elm app, which means downloading many copies of the Elm runtime.

I’d prefer to have a single Elm app that gradually absorbs surface area, including routes, from the React app. The way I see it, React would need to still have a copy of all Elm routes and just know to yield the screen to Elm. I’ve not put together a POC yet though.

In your project, did you have multiple Elm apps like I described? Did you load them on demand with dynamic imports?

elm make can take many Elm entry files and compile them into one JS file with one runtime. Each Elm entry file should expose main function.


We only had one Elm app. This app will load the desired widget depending on the flags. So only one Elm runtime.

Also an alternative way is to compile several entry points at the same time e.g. elm make One.Elm Two.Elm, this will only add one runtime.

1 Like

Ah, interesting. I didn’t realize the multiple Elm entry points worked that way, and hadn’t considered the flags option. Both sound very feasible, and now I’ll have to figure out which would be easier. Perhaps the flags approach if the eventual goal is to take over the whole app.

Thanks to both of you!

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.