Bloody genius marketing move, whoever came up with this idea.

React is Dead. Long live Reactive Rails! Long live StimulusReflex and ViewComponent!

Let me tell you about the feeling that I get when I encounter something poised to revolutionize my work as a software developer. A specific, tangible physical sensation in my bones; a tingle in my skin and a buzz in my fingertips. Like having done a little too much cocaine, if you know what I mean.

Apparently this mania hits me approximately every 5 years.

The first time was in 2000 when I read The Pragmatic Programmer and Kent Beck’s first XP book and they totally upended my ideas of what it meant to be a software engineer. Before that, to be honest, I was just thrilled with the money I was making, but craftsmanship and extreme programming turned me into such an evangelist I ended up founding Agile Atlanta.

The second time was in February 2005 when I wrote my first Ruby on Rails app together with Carlos Villela at ThoughtWorks. It was a clone of OkCupid, but for matching up people to be staffed together on consulting project teams, not romantically. I’m serious; it was called TWIX. (I still think it’s a cool idea!) Then I went on to lead some of the first paid “enterprise” Ruby on Rails projects in the world, and the rest is history.

The next time was in 2009 with NoSQL (in general) and MongoDB (specifically). Our first Mongo project at Hashrocket brought in over a million dollars in revenue, and is the origin of Mongoid.

I got the mania again in a big way in 2015 when the release of AWS Lambda made me an early and enthusiastic evangelist of serverless app design and got me so excited that I wrote a book about it. I was convinced it would be the next Rails for me, but life got in the way and trying to write a book about something that was changing so rapidly just didn’t work out. (I do plan to finish it someday, but I’m going to need some help to do so.)

Reactive Rails, eh?

Reactive Rails, assuming that’s what we’re going to call it, is my latest obsession. I think the name’s got a good ring to it, and part of what I’m trying to do here is signal-boost it.

Naming aside, this is not your mother’s Rails code. Adding StimulusReflex and ViewComponent to Rails changes everything. It gives me the same dizzying lightness of being that I felt in 2005 when I switched from J2EE to Rails to the extent that I’m blogging about it the same way.

So far I’m seeing a future where Reactive Rails means an order of magnitude less lines of code, and an order of magnitude more productivity. Ruby on Rails is already a very productive stack in the right hands, mind you, but it’s also one that has grown super complex since embracing Webpacker, along with it’s always-in-a-state-of-massive-explosions Javascript ecosystem.

Don’t get me wrong about Javascript

For the record I don’t personally have a problem with Javascript. Allow me to brag for a second, okay, since most of you reading this probably don’t know this about my history. Back in the early 2000s, before I had even heard of Ruby, I was writing complex rich client apps using invisible Java Applets and the JSObject API to accomplish what years later became known as AJAX. My framework had hundreds of thousands of lines of advanced Javascript code to implement an entire JS-based rendering subsystem in Internet Explorer, which was the most advanced browser at the time. That work was a big factor in getting me hired at ThoughtWorks, then Jboss, then ThoughtWorks again. I even hear some of it is still in production today. (Ever wonder why some companies can’t get off IE6? Yep, my fault. Sorry.)

Over the course of the last few years, I’ve actually written and maintained plenty of React code. One of the top React experts in the world worked for me at Kickass Partners. React is clearly not dead. I know it’s a behemoth and also ReactNative is super cool for what it does. But I’m calling it now, there is absolutely no technical need for Rails developers to use React anymore. Don’t even bother with it. React is dead to me, and it should be dead to you too. Because it turns out that you can have your SPA cake and it too, baked mostly in Ruby with just some sprinklings of Javascript icing.

…there is absolutely no technical need for Rails developers to use React anymore

A little bit of context

The last 3 years I’ve been focusing (sometimes full-time) on my music career as a performing DJ and electronic music producer. However, I still code regularly, mostly on side projects and and occasional paid gigs via my old consulting boutique Kickass Partners (now merged with MagmaLabs.)

Image for post
Image for post
My github activity in 2020

Promo Guru is a side project that fellow music geek Ben Fraser and I have been working on for the last six months, making slow and steady progress towards an initial public release. Until this week, it was just a vanilla Rails app with some interactivity sprinkled in using JQuery.

Last week when our 1.0 backlog ran dry, I took a step back from feature development to consider cleanup and refactoring. I was especially concerned about our main Javascript event handler file, which had just gone over 1000 lines.

I’ve been curious about StimulusReflex since seeing Nate Hopkins’ brilliant Twitter Clone in 10 minutes video. And the silly Russ Hanneman video totally put me over the edge as far as actually wanting to give it a try.

As anyone starting a new app on Rails 6 should do, I had already embraced Turbolinks and ActionCable on PromoGuru. The result was that getting StimulusReflex setup and climbing the initial learning curve felt relatively painless. The documentation is great too.

The first thing I did was to rewrite some non-CRUD controller actions as Reflexes and I was super impressed. Ben was like, “you’re not going to rewrite the whole thing before 1.0 launch, are you?”

No, that was probably not a good idea. But I did want to address some glaring anti-patterns related to our dialog boxes and audio preview player, and for that I remembered having read about the traction that a library called View Component has been getting. Plus, the StimulusReflex docs mention it prominently.

Attempts to introduce object-oriented views are almost as old as Rails itself, and I’ve never found them particularly compelling or convenient compared to just vanilla use of partials. But this incarnation of the idea seemed different, and the list of project contributors includes Tenderlove himself, so I decided to give it a go. Promo Guru is full of UX bits and bobs that feel like they want to be components anyway, so it felt like a productive experiment.

Armed with my new gems, I quickly ran into issues around trying to render components, which I fixed by upgrading from Rails 6 to edge (6.1 RC). It was painless, which is not surprising given that Basecamp, Shopify, and Github all run on edge, so it must be stable.

Once I got the hang of the pattern, I got so productive so quickly that it turned into a wild coding spree. Not only was I refactoring, I was also addressing some edge cases and adding little details that probably wouldn’t have been touched until much later in the project. I was ecstatic. I started feeling that tingle that I described a little while ago.

I’ve seen the future of Rails programming. It feels like React programming, except with less complexity…

And even though I didn’t keep careful track of my hours, I suspect that it took about 15 hours before I hit the first bleeding-edge kind of snags that I would normally expect to run into immediately. And it was at 3 am in the morning so it probably had more to do with exhaustion than the framework. Like I got up the next morning and cleared the hurdle in 10 minutes, you know?

So people, I’ve seen the future of Rails programming. It feels like React programming, except with less complexity, less boilerplate (yes, more magic) and a whole lot less Javascript lines of code.

Now a comprehensive description would literally take writing a small book, but let me try to summarize the approach in bullet points:

Reactive Ruby on Rails

  • Uses the same RESTful application design you already know and love
  • The canonical state of your application data is kept on the server-side (database and caching layer), just like a vanilla Ruby on Rails monolith.
  • You do NOT replicate any of the model layer in Javascript code.
  • Thanks to Turbolinks, runtime client state (talking about Javascript objects maintaining the state and bevavior of the UX) is easily retained across page changes.
  • All template rendering happens on the server side using traditional Rails rendering techniques (I am a die-hard Haml evangelist and have never met a client-rendering technology that I didn’t instantly despise. JSX, ugh)
  • The server-side controller layer is traditional RESTful style, but leaner because…
  • Reflexes are tiny bits of code that alter server-side state in response to client-side actions, and take care of many interactions that would otherwise be coded as controller actions.
  • When you invoke a Reflex, it does whatever it needs to do and the screen that the user is seeing is automatically re-rendered for you. I can’t overstate the magnitude of how much developer effort this saves!
  • The use of regular JS-based Stimulus controllers is optional in this approach, but you quickly realize their usefulness. When matched with your Reflexes, they provide easy hooks for aspect-oriented enhancement of server-side interactions.
  • …or you can write Stimulus controllers completely unrelated to Reflexes that handle only client-side interactions. (I’ll provide some examples below.)
  • Just like React, only the parts of the DOM that need to be updated are ever updated, yielding some huge performance boosts, aka DOM diffing.
  • For some actions you can dispense with DOM diffing altogether and change targeted parts of your UX using either CableReady’s comprehensive DOM-manipulation API, or StimulusReflex morphs. (The performance benefits are of doing that are immense!)
  • CableReady makes it simple to trigger real-time DOM changes from server-side Ruby processes, because it’s all websocket-based.
  • ViewComponent lets you replace a lot of partial templates and forces you to think about the building blocks of your UX in a componentized frame of mind (a good thing!)
  • ViewComponent view objects are plain-old Ruby objects with well-defined interfaces, meaning that they’re ridiculousely easy to reason about and test and blazing fast compared to partials (10x speedup.)
  • ViewComponent’s optional rendering functionality drastically reduces the amount of conditional logic you need in your normal view templates.
  • “Sidecar” packaging of ViewComponent means you can bundle up Ruby code, together with any related partial, Javascript and CSS code in one namespace (module/folder). I can already tell it’s a huge productivity boost compared to having those things smeared all the way up and down your project directory tree.
  • Everything I’ve described above can easily be applied in a progressive fashion, meaning that it’s not an all or nothing affair. Make the MVP of your new project a plain vanilla Rails + JQuery app, then liven it up up with the stuff I’ve described above, which is exactly what’s happening on my current project.

Progressive Enhancement

What I just mentioned in the last bullet point above, progressive enhancement of a vanilla HTML app is a big, big deal, and probably what I’m most excited about if I’m being honest with myself. Progressive enhancement is the wise choice for most new web projects, and yet it almost never happens anymore.

To the profound detriment of proud full-stack engineers like myself (and team efficiency), once the decision is made to use React, engineering is split right down the middle, with front-end specialists handling a 100% Javascript SPA on one side (React, Vue.js, etc.), and the Rails folks writing only the server API layer. It sucks.

The Building Blocks of Reactive Rails

Turbolinks promises the performance benefits of a single-page application without the added complexity of a client-side JavaScript framework. As far as I can tell, it really hit its stride with version 5 back in 2016 and has garnered wide adoption since.

Wow, guess I’m ancient enough to remember when Sam was full nerd rather than full hipster

Stimulus is what the Basecamp folks invented to augment your HTML with just enough behavior to make it shine (in their words.) Probably since I was already steeped in usage of React, it never caught my attention before this week. In fact, I felt a degree of hesitation about investing time in it since it seemed like a case of DHH being contrarian for the sake of it. I now think I was wrong about that.

StimulusReflex promises to eliminate the complexity imposed by full-stack frontend frameworks without abandoning high-performance reactive user experiences.

ViewComponent by Github promises 10x rendering performance boost over partials.

CableReady is the part of the overall picture that I’ve used the least so far, mainly because my initial impression was that it was simply a supporting library for StimulusReflex. It’s kind of RJS reborn on WebSockets. I won’t be talking too much about that, simply because I haven’t done much with it yet. However, I’m being promised that it’s an even bigger game changer than StimulusReflex.

Code Examples

Let me share some code from my first cut of rewriting the audio preview player from Promo Guru. Here’s a screenshot so you know what I’m talking about.

Image for post
Image for post
When you press play on a track, it pops up in a fixed position along the bottom of the screen and streams that track to the user.

There are two view components reflected in this screenshot.

  • A play button
  • The player fixed to the bottom of the screen

I’m starting with the view component because I can actually show you what it looks like, and then we’ll get into server-side interaction.

Image for post
Image for post

I’m planning to make a lot of components, so I’m organizing them into modules representing functionality. So far I have audio and dropbox but in this blog post I’m only going to talk about the audio components.

Note that I’m using sidecar assets, meaning that I include Javascript and CSS files alongside components. This makes them easy to find and keeps things well organized. (If they weren’t here, they’d be under a components folder somewhere in assets.)

There’s a couple helper methods sprinkled in there, but the only one relevant to this blog post is reflex_link_to

a lot of gobbledygook for creating a link that invokes a reflex on the server.

Okay, so here’s where it starts getting interesting. In the first iteration of this functionality, we simply shoved a hidden player into the partial template for a track. That was grossly inefficient both in terms of rendering performance and bloating the size of our markup, but it worked well enough for an MVP.

When I wanted to refactor it into something more efficient and elegant, my first inclination was to make a single player that always lives at the bottom of the screen, with a Javascript API that I can call with parameters corresponding to the track being played.

That seems normal, right? But it would involve writing a bunch of Javascript and thinking about the (object, not user) interface of the player, and stuff like that. And besides, I already had a player that worked as long as it had full access to a track instance.

So here’s what I ended up doing.

The key to Reactive Rails design is remembering that the server is the canonical source of truth for the state of the client (or something like that.) Even when it’s not obvious!

Taking that advice into account, when there is a track playing, a track object could live in a @track_to_play instance variable. And at other times it could be null.

Here’s how that approach plays out in the application layout.

The most interesting line there is 13, right smack in the middle.

<%= render Audio::Player.new(track: @track_to_play) %>

Note that to get render to accept a component object instance without ActionView freaking out you need Rails 6.1, which as of the writing of this blog post (October 2020) means you have to be running edge. As far as I’ve heard, Basecamp, Shopify, and maybe even Github run on edge. If you’re running a released version of Rails, there’s some workarounds and stuff that you’ll have to Google, it’s not worth getting into here.

So what was I saying? Oh yeah, I always render the component and sometimes the variable @track_to_play is nil because the user isn’t listening to a track, and that’s fine, because if it’s nil, then the component knows not to render. All without involving any conditional (if/else) code in the template.

class Audio::Player < ApplicationComponent
attr_reader :track
def initialize(track:)
@track = track
end
def render?
track.present?
end
end

That render? method is part of the ViewComponent API. It’s hard to overstate how much conditional logic it will let me take out of my templates.

Moving on, how does @track_to_play get set? Using a Reflex!

Image for post
Image for post
StimulusReflex is super cool, so I already have a bunch of reflexes

When you invoke a Reflex, it does what it does, and then StimulusReflex automatically re-renders the current screen and sends it down to the client, where DOM diffing magic is used to figure out how little of the screen we can get away with refreshing.

Click the play button and presto, changeo! The player appears at the bottom of the screen and starts playing. Speaking of the play button, here’s what that looks like in use.

Non-server Stimulus controller actions

Before I wrap up let me show you how regular (non-Reflex) Stimulus actions are implementing. The player has previous and next buttons. Our original code had a bunch of Javascript backflips to find the right player and hide any other players that might be playing and etc.

With the new reactive approach, all I need is to somehow change the state of that magic @track_to_play variable on the server and I should be good. Everything else should happen automatically.

First I need to write some plain-ole Stimulus actions.

The most important things to notice in this code, extracted from the player’s template, is that the top level element has a data-controller attribute that identifies which Stimulus controller to connect to this element. It’s what will receive stimulation, so-to-speak.

When you mix Stimulus with ViewComponents, make sure to note that module names will turn into prefixes in your markup. In this case, the name of the controller is audio--player not simply player. (This is the snag that I hit at 3 am in the morning that forced me to call it a night.)

Looking at the next action, it reads like this click->audio--player#next

So when a click event happens, invoke the corresponding action on this element’s Stimulus controller.

The summarized explanation of this code:

  1. Get the track id from the .audio-player root element. (Line 2)
  2. Find its corresponding play button on the screen. (Line 3)
  3. Navigate the DOM to find the next play button on the screen. Yes, this needs more code to tighten it up. (Line 4)
  4. Use thestimulate function to trigger a Player Reflex play action on the server.

That’s literally all there is to it. I know this is going to get more complicated as we continue to add features, but I’m satisfied that I’m going to be able to grow it in a clean and organized fashion.

A little extra StimulusReflex goodness

Dropping this in here not so much because it has to do with the whole reactive Rails idea, but just because it’s damned cool.

These are application-wide hooks for all reflexes. I’ve got a couple things going on so far.

  • Add and then later remove a wait CSS class to the body element whenever there’s server activity going on. This lets me easily animate a progress indicator and implicitly let the user know that something is going on that they should wait for.
  • Some performance benchmarking. So far I’m very impressed with the responsiveness of the approach.

Minor Gotcha

At the moment, Rails 6.1 and ViewComponent are not playing completely nicely together. If you do everything I showed you above, you’ll quickly figure out that picking up changes to ViewComponent templates requires a server restart. Ouch.

I did a little digging and was able to come up with a workaround pretty quickly:

It appears to be a regression, so I’m guessing it will get fixed sometime soon. The workaround is not the worst in the world, either.

Conclusion

This feels like the start of something big for me. Already thinking it’s got enough meat for a new book. And it seems like the community is gearing up to become a movement, not just a niche. So it’s probably a good time to hit that Follow button if you’re interested in what happens next!

Image for post
Image for post

And oh, one more last thing…

For reading all the way down to the bottom of my blog post, I want to give you a little gift!

Written by

CTO for Hire, Author, and Software Engineer. Digital Nomad. Chief Consultant at MagmaLabs. Electronic Music Producer/DJ.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store