Reactive User Interfaces

Creating maintainable interfaces with React & Meteor

We often explore new technologies to help us deliver the best experiences possible. The past year has seen a glut of new reactive frontend technologies like React, Polymer and Angular 2.0 enter developer consciousness. When we investigated React we discovered its disciplined approach to reactive UI construction paired with a realtime backend (such as Meteor) has the potential to create delightful and maintainable apps.

Reactive Templating

Reactive, or declarative templating is a style of user interface (UI) construction that contrasts with the imperative approach championed by libraries like jQuery.

Declarative templating separates UI state, the underlying logical situation of what the user can see, from the layout being viewed (DOM). You declare how the rendering technology should translate your state into the visual elements of the DOM. From then on you simply update the state and the DOM adjusts automatically.

Declarative Templating

Imperative technology includes state in the DOM. Thus pains must be made to ensure that the DOM state is manually kept up to date. For example, consider the unread email count in Gmail. If Google implemented the unread email UI imperatively, this is what it would look like.

Imperative Templating

  • When an email is read, we need to:
    1. Change the appearance of the email to indicate that it was read
    2. Update the unread count
  • When we check and find a new email on the server, we need to:
    1. Draw it in the UI
    2. Update the unread count
  • When we delete an email, we need to

    1. Remove it from the UI
    2. Update the unread count

Something as common as an unread count would need to be changed in many places and depend on a variety of states. The complexity of an imperative UI in a realtime system asks for errors and inefficiencies. A declarative rendering framework manages complexity with an internal model of the core state of the page (the list of unread emails) and automatically changes the UI to reflect this model.

Reactivity & Meteor

Despite the fact that reactive UIs make for cleaner, more reliable frontend code, they’re often considered nice-to-have’s for traditional applications. However, modern products built on reactive backends like Meteor are nearly impossible to deliver without reactive frontends.

Meteor is a realtime framework that provides a constant stream of updates to the data model which could come down the wire at any time. In contrast to the user-initiated interactions in the unread email example, Meteor allows any part of our UI state to change at any time to reflect the state of the system. Trying to imperatively reconcile and patch changes to the system would be a nightmare.

Approaches to Rendering

It’s no surprise then that Meteor ships with it’s own reactive rendering frontend: Blaze. There is a key difference in the way Blaze and React work that makes React promising for Meteor projects.

Blaze piggy-backs on Meteor’s dependency tracking system, Tracker in order to keep track of which parts of the DOM correspond to which parts of the UI state. This means when you change state (e.g. delete that email), it can know exactly which DOM elements to modify. Updates flow from the event directly to the nodes that need to change. While this architecture makes it intuitive to understand what will re-render given a change in a basic system, the origin of changes becomes unpredictable in a large interdependent app.

React uses a straightforward system called the Virtual DOM—a tree of React Components, each of which contains its own state and that of its parents, and corresponds to real DOM elements. Updates always flow up the Component Hierarchy, then down to the relevant nodes. When modifying state, the least common ancestors of all elements that care about that state are also modified. This means that a lot of unrelated elements could be altered as a result. However, React’s diff algorithm mitigates unrelated changes from propagating by calculating the minimum possible Virtual DOM alterations necessary to achieve that change in UI state. Only then does it adjust the real DOM.

Reactive User Interface

React’s Advantages

React’s method isn’t as expensive as one might think. Not only is processing in memory via JS much faster than making changes to the DOM; it presents significant advantages.

Simple to Reason About
There are clear rules that govern how data flows and how it affects the UI. The rules are reinforced by the consistent and stringent way components are put together. The result is intuitive frontend logic.

For instance, consider the Leaderboard example Meteor app which we’ve re-coded in React. When the user clicks the “Add 5 points” button, we pass the change back up to the leaderboard component which is the least common ancestor of all elements that care about the player list. The leaderboard component then takes responsibility for updating the players and flowing the data back down to the various components that need to render them. The diff algorithm ensures that it’s done in an effective way. The rigid internal model helps avoid bugs and makes engineers more productive.

Predictable Reusable Components
React’s top-down data flow results in a natural separation of components. It’s designed from the ground up with clear guidelines for how components interface with each other, thus leading to consistent predictable code. Large teams and projects benefit from disciplined modular architecture that makes component reuse natural. A bonus is they are also easier to isolate and test! The first step to true interoperability on the web is true component libraries.

Server-side Rendering
The diff and patch model makes server-side rendering trivial to implement in any stack. Thanks to Meteor’s isomorphic APIs it’s even easier.

if (Meteor.isServer) {
  var bodyHtml = React.renderToString(<Leaderboard {...props}/>);
  // return the HTML from a server-side-route
} else {
  React.render(<Leaderboard {...props}/>, document.body);
}

The combination here is sublime:

  • React’s renderToString quite simply outputs a string of HTML, ready to be served to a google bot, or to render to the user immediately.

  • Meteor’s isomorphic APIs mean that in many cases, the definition of <Leaderboard> does not need to change at all!

Checkout Meteor’s leaderboard paired with React. In a later blog post, we’ll show in detail how the process of server-side rendering and hydration can combine in Meteor to get the best possible behaviour for a single page app.

What about performance?

You’d expect that React’s approach of propagating changes down the component tree rather than directly changing the components that care about an update would be wildly inefficient. In a deeply nested version of the animation above, there are many more changing ancestor nodes that would never be touched in the Blaze example. However, as Facebook engineer Christopher Chedeau explains, performance is great due to the Virtual DOM speed and simple heuristics. The key points:

  1. DOM changes are fairly predictable
  2. Component trees are never that large

In other words, the DOM is so sluggish that doing work in JavaScript doesn’t even factor into the performance equation.

Conclusion

It’s clear that React can help you build maintainable interfaces that scale. However, it’s not opinionated about how data is moved between server and browser. This is where Meteor shines. Meteor’s approach to latency compensation & automatic data propagation pairs well with React & the Flux architecture. We’ll explore how in a future post.

React is fantastic for managing large reactive front-ends. Meteor is an unparalleled for transporting data in realtime. Combining both gives engineers the tools to craft maintainable cutting-edge interfaces. There’s no better way to build the rich applications modern users demand.

Reference
Meteor Leaderboard in React
React
Meteor
The “offical” React-Meteor package (still a WIP)
Why React?
What is React? by Evented Mind (video)

Get the Newsletter

19 Comments

  1. Steve

    Great article Tom. Thanks.

    I’ve been using Meteor since the v0.6.x days and more recently have come around to React – and was just thinking: wouldn’t it be great to combine the two? Making the UI components reusable just ups the ante for Meteor IMO.

    Any recent developments/insights on server-side rendering? I noticed that PR (for the webapp package) never got merged…

    Jul 13, 2015 Reply
  2. Emanuel Nedelcu

    After a couple of years I’ve started to look again on Meteor and I really like the Meteor + React setup.
    Here is a small demo in which I’ve played with routes / 3 way data bindings / drag and drop / accounts-ui.
    Not sure I’ve done things the right way, since I’ve just learned React and it’s the first project I’m doing with Meteor and React.
    (I tried something similar with Firebase + Angular but I like Meteor + React better).
    http://react-meteor-boards.meteor.com/

    Mar 31, 2015 Reply
  3. Giant Elk

    How do you transform the JSX to JS in this example?
    https://github.com/percolatestudio/react-leaderboard

    Mar 30, 2015 Reply
    • Tom Coleman

      The `reactjs:react` package does it automatically. It’s pretty cool.

      Apr 6, 2015
  4. Sven

    Very interesting Tom :) I’ve also worked with React and find it awesome to work with, and as a bonus it’s also improving my JS skills.

    Three questions:

    1. In your reply to Louis you mention that you use React Router, do you have any examples how to use it? I’m especially curious about user authorization. What I would do is use the static method willTransitionTo and check for Meteor.userId(), if false use a redirect. This is in theory, I haven’t had time to play with it unfortunately. Thoughts?
    2. Do you see any (big) advantages in using Arunoda’s Flow Router instead of React Router?
    3. Are the template level subscriptions available in react? Or should we roll our own like: https://www.discovermeteor.com/blog/template-level-subscriptions/

    Take care!

    Mar 20, 2015 Reply
    • Tom Coleman

      Hi Sven,

      1. I’d be inclined to pass the user in as a prop of the whole app (reactively somehow — in Flux you might have a user store), and handle the job of rendering a different page at the component level. But we’re still playing with stuff in this area.

      2. I guess flow router has tools to handle subscriptions reactively for you. On the other hand, it doesn’t do any rendering at all (which is probably good as you want to render components, not blaze templates), but react-router is nice in the way it handles stacks of routes and layouts.

      3. The react-meteor package I linked to has support for template level subscriptions.

      Mar 25, 2015
  5. Idris

    Tom, thanks for this well-written article. You should join the #meteor channel of the Reactiflux Slack chat: http://reactiflux.com

    Mar 18, 2015 Reply
  6. Simon Mansfield

    Really interesting read, I’m looking forward to the follow-up article(s)!

    Mar 9, 2015 Reply
  7. Fabian Vogelsteller

    The clear logic of propagating changes is nice, but with react you loose the clean separation of views and controllers. Its like going back to PHP mixed up with echo = ”. Its not easy to clearly distinct the DOM tree generated, which makes it hard for designers to make changes to the layout.

    I agree that complex applications with lots of reactive data sources can be challenging.

    Mar 6, 2015 Reply
    • Tom Coleman

      I think the argument is that it’s a false separation of concerns because the templates and the “controllers” (or whatever you call the collection of helpers and event handlers that go with them, controller is a very overloaded term) are so tightly coupled that they really should be in a single file.

      Mar 12, 2015
  8. Arunoda

    Hi,

    I mostly agree with your comments. But I don’t agree with the Blaze and React comparison. Yes, React does use a virtual DOM and only do the changes it wants. But in really there are more things other than DOM changes in the UI (I mean in the components level).

    So, it’s better if we can control how components are rendered on the screen. That’s can be done with Blaze as well. The issue with Meteor is not blaze, it’s the routing system and the APIs we used to interact with rendering.

    We’ve started working on this and build some tools for Meteor. We didn’t do any comparison with React, but we could gain a lot gain compared with normal Meteor apps. (using IR).

    Let me share those tools.

    1) Flow Router – https://github.com/meteorhacks/flow-router
    2) Flow Layout – https://github.com/meteorhacks/flow-layout
    3) Flow Components – https://github.com/meteorhacks/flow-components

    Mar 6, 2015 Reply
    • Julián Á

      :O ! This is very new Arunoda :D …as always sounds very awesome!, the flow-layout if works will be OMG !!!

      Mar 6, 2015
    • Tom Coleman

      This is great Arunoda, thanks for sharing them!

      Mar 12, 2015
  9. Morgan

    Great article Tom, thanks thanks for sharing!

    Mar 6, 2015 Reply
  10. Kendal Smith

    Very convincing article. What is remaining to be done to make this a usable package?

    Mar 5, 2015 Reply
  11. Louis DeScioli

    Good post Tom! Cool to see other people in the Meteor community using React, especially y’all. I’ve really enjoyed the abstraction that React gives me between the UI and the model. And with the ability to make isomorphic models and controllers with Meteor it’s never been more clear how to reason about the state and behavior of a distributed application.

    I’m curious for your thoughts on a couple things:

    – Iron router vs react-router? Especially curious since you’re one of the biggest contributors to Iron Router
    – Have you had to handle how to manage subscriptions across components? One idea that kind of excites me is the ability for components to declare their own subscriptions. Instead of using the default DDP connection with Meteor.subscribe, you could hand a component a DDP URL or connection as a prop and it could call its subscriptions on that connection.
    – Have you messed around with rendering React server-side on a Meteor server and giving that to the client? From my understanding of the source, as it stands the `boilerplate-generator` core package is tightly coupled with templates and there’s not an API to override the default Meteor server response

    Mar 5, 2015 Reply
    • Tom Coleman

      Hey Louis,

      Thanks very much!

      1. We’ve been using react-router. Most of what IR does is rendering and layouts and of course that’s redundant, so we’re steering clear of adding all that complexity. There’s probably an argument for using it for subscription management, but I don’t know if IR does the best job of that anyway, TBH.

      2. That’s a really good question. I’ve not settled on the best pattern for now (and as I alluded to there’s a future blog post in the works that talks about this), but I’ll mention two interesting ideas that we’ve been thinking about:
      a) Using Flux Stores as marshallers of data — they’d subscribe and talk to Collections. Components would simply talk to stores in *exactly* the way that a non-Meteor Component would (i.e. over event emitters).
      b) Using Relay and GraphQL to auto-subscribe based on the component tree. Don’t have enough space to go into it now (and TBH, Relay is so new and blurry that it might not even make sense), but it’s kind of an interesting idea. Much more to say about this, still mulling it over.

      3. If you check out the leaderboard example, you’ll see that we do exactly that. We needed to fork the `webapp` package — we’ve got a PR upstream to try and get something in a future Meteor release that makes this truly trivial (https://github.com/meteor/meteor/pull/3860)

      Mar 6, 2015

Previous Post

Meteor Packaging Q&A

Meteor core engineer Ekate answers all our packaging questions

Next Post

Architecting Better Landing Pages

Building robust static sites in React