In an attempt to bring some of the benefits of Elm to React, you can use Immutable JS to ensure that all state is immutable and Bacon JS to enable streams of events to handle the controllers. This approach differs slightly from the Elm Architecture approach in that the controllers are completely removed from the views (this approach doesn’t need a single onClick or similiar attribute on any DOM elements).

Elm’s approach to UIs is becoming increasingly popular for it’s simplicity, clarity, and reliability (although the excellent type system helps somewhat here). In particular, having the state in a single immutable data-structure helps to avoid trippiing over yourself as the state changes as you no longer have to worry about the order in time that changes have been made to reason about what the result should be. Reactive events (Streams in Bacon JS and Signals in Elm) enable you to treat events as values in time that can be filtered to create new streams and mapped to alter the values. With the controllers handled like this, it’s simple to understand what the outcome of various events should be. Finally using a reactive DOM library (VDOM in the case of Elm) not only has perfomance benefits thanks to the diffing of the changes that should be made to the DOM, but also provides a simple abstraction layer that ties in nicely with the event streams to make the view layer simply another transformation on the state (albeit one that has an effect that is rendered on the screen).

UPDATE: I've setup a simple React boilerplate for this, available here.

Models Views Controllers

The basic pattern is simply a variation on the classic MVC pattern.


Starting with the controllers, the selectors for the views are stored together so that they can be used by both the controllers and the views. A Bacon Event Stream is created from events on the document that are then filtered by the ID or class and mapped to a functor that performs an action on the previous state:

updateMessageFieldId: 'update_message_field'

// Events
var keyups = Bacon.fromEvent(document, 'keyup');

// Controllers
var updateFieldEvents = keyups.filter(function (event) {
// Filter by the ID of the element
return === SELECTORS.updateMessageFieldId;
.map(function (event) {
// Return a functor to alter the previous state
return function (previousState) {
return previousState.set('message',;
// The main eventStream that merges all Controller streams into one
var eventStream = Bacon.mergeMany([


The models define an initial state and a current state stream that is defined by taking the eventStream and folding (eventStream.scan) it over the initial state to perform the actions on it:

// Define the initial state of the app
const initialState = I.fromJS({
message: 'Hello World!'

const currentStateStream = eventStream.scan(
function (previousState, eventAction) {
// Call the functor from the controller
// to return a new altered version of the previous state
return eventAction(previousState);


Finally the currentStateStream is used to call Reacts render function to update the DOM with the evaluation of the React views with the updated state:

currentStateStream.onValue(function (currentState) {
// Render the main React component on changes to the state to update the UI
render(<App selectors={ SELECTORS } state={ currentState } />, containerElement);