StimulusReflex, or LiveView for Rails
docs.stimulusreflex.com
docs.stimulusreflex.com
I'm very interested to learn what has changed since then that made this possible, or made it work better. Or whether those problems are just being "lived with".
See this https://evilmartians.com/chronicles/anycable-actioncable-on-...
Looks like having 20,000 idle clients connected to a server sits at only 380MB and Erlang is at 737MB.
That's with Go, versus with anycable itself. Anycable-go looks like 798?
Cant find the link to the article though.
I have never in my life been as productive as I am inside of a Vue/Tailwind code base at this point.
One of my clients is a fairly vanilla Rails 4 app (which is arguably a friendly place to be - although slow) and it’s not even close. I miss Vue and having a full “app” environment on the front-end side constantly. Jumping back and forth between these projects is like going from a hot and steamy comfy jacuzzi into an icy cold pool.
For what kind of app? Crud, frontend to a SaaS, something else?
I am still finding form-heavy apps needing frontend+backend form validation to be faster to write entirely server-side. Which is painful as more and more I have field types that are best off as JS widgets, and enhancing server-rendered forms is a pain.
Otherwise, the point is that you won’t have to jump back and forth—you can just stay on the backend. I’ve actually never used Stimulus Reflex, but in LiveView, I’m writing very small bits of JS maybe 2% of the time.
For people who haven't already climbed that mountain and learned a new "language" I agree with GP. This could be a game changer.
My only regret is that for a while now JavaScript had basically been the standard. As good a development as I think this is, it will really in more fragmentation and less general purpose devs.
Nevertheless I think it's good, and web assembly was always going to do this to JavaScript ecosystem anyway.
Check the StimulusReflex Expo for interactive demos w/ code samples: https://expo.stimulusreflex.com/
A lot of people have much worse ping times than that. Try using this on a satellite connection for example, and your experience won't be very pleasant.
On my internet connection, which is pretty average by polish standards, the best ping I can get is 30ms, and that only happens when the server I ping is just one or two hops away. I probably won't be able to go under 100ms when the server is in the US.
It seems like this approach sacrifices runtime performance for development speed. I feel like it might often be a good tradeoff to make, but it's a tradeoff nevertheless, and we should recognize that. Separate layers for frontend and backend introduce complexity, but client side code is client side code, you can't go much faster than that.
As for replacing rich frontend rendering libraries in more complex apps - it seems dicey. Part of the virtue of React is that it simplifies the visual presentation of the frontend based on a constellation of data. Things like highlighted elements, hover states, and dragging items. It seems difficult to enable that level of polish while the backend is making dom-level changes based on the explicit data that you have to deliberately send over the network.
But it's true, even though our app is pretty straight forward crud, we still need things like autocomplete, drag and drop, tabs, modals, form validation etc. Just having js being js and ruby being ruby seems easier for me to parse and work with.
AFAIK there's no equivalent for this in Java and I mean the same not kinda-the-same but not really like Vaadin or God forbid GWT.
Whereas with Elixir, it's handled by the way failed processes are handled which is to just "let it die". Another one will come along and replace it. (I'm greatly simplifying here)
Moving everything to the server instead also closes the gap, but the obvious problem is that this introduces a lot of latency for UI interactions. Ajax page loads/Turbolinks works so well because it actually makes things faster with almost zero effort. Introducing a similar method for every UI interaction though is a different matter, and I don't see how the latency won't be noticeable.
For instance, The TodoMVC example's server seems to be far enough way from my location that creating a task has noticeable lag. Sure it's just an example, but without introducing some client-side/optimistic rendering, that's going to be hard to avoid (and that would defeat the purpose of having all your logic in one place, on the server). Not sure how this is addressed here?
[1] https://dockyard.com/blog/2020/12/21/optimizing-user-experie...
[2] https://changelog.com/posts/petal-the-end-to-end-web-stack
As for latency, this is over websockets. It becomes a problem if you are connecting to a server across the ocean but otherwise, it's incredibly fast and feels just as snappy as JS. I actually also haven't used Stimulus Reflex, just Phoenix LiveView so I'm not sure if they are exactly the same, but the TL;DR is that they are sending VERY tiny payloads over websockets. This video explains it well (though it's also a demo of LiveView): https://www.youtube.com/watch?v=MZvmYaFkNJI
And I realize this is using websockets, and that the payloads are tiny, but you can't fight the speed of light ;), so it's more of a latency concern (vs bandwidth). So in order to truly keep all the state and rendering with the server, even the smallest UI interaction would require a round-trip, and unless you'll have end nodes all across the country, that seems like it will noticeable.
With some actions like "search", "save" or "reload" I would expect a spinner and delay anyway, so that's not a problem. But if opening a dropdown menu or adding an item to a list (like in the example) feels slow that might not be the best user experience. But perhaps it's not a problem in practice, I haven't seen too many examples.
But yes, there are many cases where this will not work. It's just a verrrrry attractive option when it can.
If you have a server in NY and you happen to live in Germany it's going to take about 100-120ms to do a network round trip in a best case scenario (high quality wired cable connection) even with a 1 byte payload. For most websites running in 1 datacenter that means a massive population of the world is going to feel that latency.
That's why I'm not sold on using LV and websockets for everything (such as transitioning from page A to B, etc.). Hotwire Turbo Drive / Frame uses HTTP which means you can cache responses and send back 304s when the content doesn't change. HTTP feels like the right protocol to do this, and then you can save Websockets for when you need to broadcast relatively small amounts of new / updated / removed content to 1 or more connected clients. That's what the Hotwire model provides.
Stateful (I mean really keep things in assigns) sounds like an inefficient caching. Not all assigns are per user, caching the same things on each process is redundant. And to share these things as one set of data is to put them somewhere else, most likely in a process under the root supervision tree. And there's less point for using LV.
Folks forgot how fast Phoenix.View rendered from controller was (a hello page is microsecond!) it's pretty damn good.
Unless I misunderstand - that's still a round-trip to fetch and return a 304? Or are you thinking an edge cache closer to the end-user?
(I don't disagree that websockets aren't a universal solution - but if used as server push - it's difficult to see how plain http could have lower latency? Long polling would be similar, but probably (even) harder to scale if you want 10k+ open connections?).
But, I have to admit, I don't love it. I would love to have an integrated environment where client/server doesn't matter, but I don't think this gets far enough. Working in a spread of html.erb with special tags plus some Javascript doesn't feel great to me compared to React.
I like their TodoMVC app, since that gives a simple, minimal starting point. Getting persistence in that few lines of code is nice.
However, it has also at least one bug compared to canonical TodoMVC. I think the problem of this kind of too-clever binding is that it makes this kind of bug easy to introduce; whereas something with clear one-way data-binding like React, for all it can be verbose and annoying, tends to make it easier to get this sort of thing right.
(The bug? Click an existing todo to edit it, start typing, then press escape. The edit is meant to be discarded, but instead you'll see a flash of the old value and then the new value is persisted. It's not the worst bug ever, but shows that even in a simple app, experts can subtly screw up important behaviour.)
TodoMVC is meant to be a minimalist demo, not a battle-hardened app.
We switched from React to Reflex 7 months ago. We have fewer bugs than we did with React, and we ship things about 3 times faster.
5/5 of us strongly prefer Reflex over React. Including one of us who was a big React aficionado beforehand.
Just wanted to put that out there for anyone considering Reflex, a bug in TodoMVC is not a harbinger of a bad framework.
Personally, I enjoy React on the front-end but don't currently like any back-end framework, so Reflux may well be better in aggregate.
(FWIW, I have a PhD in computer science and big part of that was on web applications and frameworks for them, combined with ~15 years commercial experience mastering web development, so this wasn't meant to be a middle-brow dismissal. As I said, it is exciting, I'm just not convinced.
I think there is some merit in the argument that too much cleverness can be dangerous. Battle-hardened is tricky; I agree TodoMVC is meant to be a minimalist demo, but if a minimal demo can have subtle bugs caused by the cleverness of the data binding, how much more so a real app with a much more complex data model?
But I guess it does eliminate whole other classes of bug, so I can believe it works less buggy and more productive better than React+some back end.
I certainly find the current front/back split endlessly painful. I am also excited by the new React Server Components which might be another good solution to that.)
EDIT TO ADD: I guess what I was trying to say is: I found this interesting, and I looked at it, and then I found this bug in the example, and I wonder if this framework might make a bug like that more likely because the power of making some things easier is obscuring what is happening in some cases, and I don't know if that trade-off is worth it.
Switching from React to Reflex isn't just switching a framework, but a whole stack.
I'm curious why would one would choose this over Phoenix LiveView for a new project? How are the two different?
also, StimulusReflex predates Hotwire for 1 year and is already pretty hardened :-)
It's not that dumb in practice because for a lot of things Hotwire uses HTTP instead of WebSockets which means you can take advantage of what HTTP has to offer such as caching.
That means if you decide to dynamically load a menu's contents with a Hotwire Turbo Frame then it's only sent over the wire once and assuming its content doesn't change, it'll serve a 304 content not modified for future requests.
For pushing updates with Turbo Stream which is done over WebSockets, I'm pretty sure it'll push that snippet of HTML (let's say a user's comment) over WebSockets and then either append or prepend it to the DOM based on however you configured it to be inserted. And if you edited that comment later on, it will replace that snippet of HTML that was previously sent (since it's the same ERB template). It won't re-render the full page.
It’s just... people think those technologies are congruent but the overlaps are actually minimal.
For any rather sophisticated UI you can (and will want) to use both.
For example: repaint a data visualization. With Hotwire you can only replace it and start fresh, with SR you can just surgically morph data attributes without disconnecting your stimulus controllers
Basically all you to is to call a remote procedure, which you can do by adding a simple `data-reflex="click->Todo#toggle`. In the remote method you change something about the state (for example db, redis). The server rerenders the page and morphs the difference.
If you need more control you can get it by defining which elements to render and to morph.
Hotwire is for replacing frames with content when navigating through anchors or form posts.
StimulusReflex is performing RPC, then diffs the old page and the new page, updating changed elements.
Turbo also has the possibility to gracefully degrade when js is not available (although I doubt this will be taken advantage of much in practice).
Especially for an established rails app, allowing web sockets can require architecture changes.
Now that Google will index SPAs, I'd strongly consider a SPA where the server just generates meta tags (for SoMe link previews etc)
Isn't this kind of a good thing, though? Now your UI can't lie to you about the true application state, making you think something worked or completed when in fact it's still interacting with the server. /s
I like SPAs because they can work offline / on spotty network connections. I don't like SPAs because they are often unclear about whether or not they are fully synchronized with their backend. I kinda wish people writing SPAs just wrote totally offline apps first that then had an independent synchronization mechanism with their respective backends as a general rule.
It's a SR clone, the maintainers are actually very active in our community :-)
(I'm the maintainer).
Here's an example [2] of using it for infinite scroll (instead of pagination).
I'm pretty excited to try out django-sockpuppet from an adjacent comment!
[1]: https://htmx.org/ [2]: https://engineering.instawork.com/iterating-with-simplicity-...
It's 500ms to 1000ms for something that ought to be instantaneous like clicking on a tab that I have already seen.
React websites might be a larger initial download, but its only an extra 3 seconds wait to begin with, then near instant reactivity afterwards for everything that doesn't require a network call.
Ironically, the browser cache is much faster than 500ms so the tab demo would have been faster if implemented as a full page refresh.
The Rails community has spent years resisting the future. Ember was an attempt to jam Rails into JavaScript and it's been a miserable, confusing, slow mess that every team utterly regrets.
Rails has spent years and years pushing server-side rendered partials. Now the Rails community has yet another "new" approach that involves writing yet more Ruby instead of JavaScript.
If you adopt any of these Ruby-for-frontend solutions, your future efforts to scale will be severely hampered as you've glued so much stuff down in Ruby. Plus you'll have trouble hiring JS developers, because who wants to learn something so unserious as this?
Write your web frontend in JavaScript. Either be a polyglot or create silos.
So they like writing more Ruby instead of JavaScript. They've enjoyed Rails pushing server-side rendered partials.
And the person that comes demanding they rewrite all their frontend in the current JS framework-du-jour (possibly to be rewritten again in a couple of years) will be told to fuck right off.
But we still have the requirement of "near instant" front-end for certain things. And this is precisely the sort of thing that a team of RoR devs can really get behind and do well. Rather than try and cram yet another JS paradigm and DSL into their heads only to be forgotten when the next one rolls into town.
Personally I'm a Phoenix/LiveView champion, but we're a Rails shop and I know better than attempt The Big Rewrite. So this really fits the bill.