Elevator Pitch
CableReady and UJS (as Mrujs) are not only viable, but arguably better options when it comes to future-proofing your app. Choosing CableReady & Mrujs fixes the most annoying problems of the Rails 7+ frontend story.
Description
Want to level up your frontend with the highest ROI? Start with how the frontend manages updates! Let’s revisit CableReady and UJS, as an alternative to Hotwire and Turbo. I’ll talk about their future-proof design choices and how they can be extended to handle just about any problem you throw at them.
Notes
This will be a condensed version of this guide on why I think CableReady & UJS are better technical choices than Hotwire/Turbo: https://thomascannon.me/guides/fixing-the-rails-networking-stack
The core points I’ll cover are:
- What a good networking stack looks like for web apps: From basic functionality to “full featured” using WebSockets
- a high-level summary of how I design to make that networking stack seamless
- Some of the core design constraints of Turbo, and how they’re very hard to work around
- Heavily reimplements the browser’s own networking and history stacks
- Uses a bespoke protocol that is hard to adjust
- Implicitly enabled by design
-
The following opinions from Konnor Rogers:
where I think Turbo went wrong is the following: - Links should only be able to make
GETrequests -<turbo-stream>should be a<turbo-action>, stream is just too confusing. It makes people think they’re using a web socket when it’s really just a DOM operation - Turbo frames are clunky with managing IDs. Scoped navigation as the default is equally weird. - There are like 40 different combinations of meta tags. It’s so hard to keep them all straight.
- What CableReady is, and more specifically, CableCar
- Show a comparison of payloads, show how CableCar is extensible and understandable for anyone who’s done any amount of client-side work (so not Rails specific!)
- Show how the same changeset is transmitted across multiple mechanisms. Also show some extensions to handle common gotchas.
- Designing plain HTML fallbacks for when JavaScript is finicky and acts up
- Example: “confirm destroy” flows, where the confirmation is its own view, with a form that submits the actual destroy action
- Use CableReady to insert the form where needed (eg: a dialog) on a remote request
- Reminder that Progressive Enhancement doesn’t have to be perfect, but is there to prevent someone from being blocked for as long as possible
- Talk about cases where JS is unavoidable (eg: Stripe Element)
- Example: “confirm destroy” flows, where the confirmation is its own view, with a form that submits the actual destroy action
- How Mrujs is a future-proof JavaScript approach (compared to Turbo/Stimulus) because:
- It’s designed to augment the browser’s behavior
- It’s based on event handling/dispatching - Takes up as little space as possible
- operates on the assumption that you might not need it in the first place.
- Call back to Brian Childress’s Blue Ridge Ruby talk , where he pointed to an example where “real-time updates” actually means “8AM, once a day.”
- Re-emphasize that you might not need full reactivity, so aim for simplicity.A networking stack that’s designed to work seamlessly within the massive landscape of JavaScript libraries fits in line with that.
- Documentation & maintainability
- CableReady is extensively documented, much more than Hotwire. Also less volatile because it’s “feature complete.”
- One ethos of Rails is to use sensible, easily understandable behavior in 90% of cases. CableCar is a protocol that does that.
- Rails also advocates for “using boring technology/patterns.” CableReady/CableCar is infinitely more boring than Turbo/Hotwire.
- Browsers are complex systems, and using their universal mechanisms allows more developers to work on Rails apps and encourages the growth of the ecosystem. Much more than a bespoke frontend framework.