Whenever I’ve been hyping up Hotwire, people have been pointing me towards HTMX which is similar and seems even more awesome.
but… I wanna change something in HTMX.
In this example, if the attributes were real hrefs, the page would work (just slower and with more flickering) with JS turned off, too. That’d be awesome♥
HTMX and Hotwire are at their worst when they are just JS with angle brackets, when they just are a tags-and-attributes DSL that’s still dependent on the same old SPA DOM stuff. They’re at their best when they can help us make Lynx-friendly, wget/XMLstarlet/edbrowse, scrape-able, old-fashioned websites that just happen to get a li’l faster, smoother, more polished when you have JS turned on.
When I wrote brutaldon, I used intercooler for progressive enhancement (so things like favs didn’t require a page refresh) in exactly this way, and it still works with HTMx. The only downside is sometimes having to repeat yourself (same URL in href and hx-get).
Oh, hi, I didn’t know you were on Lobste.rs ♥!
What I’d want is a HTMX that could hook into certain href atts instead of having its own “hx-” att prefix.
Brutaldon-style progressive enhancement is exactly what I have in mind, yeah.
Yeah, it sucks a little to have to repeat yourself. It’d be nice at least for a blank hx-get or hx-post to get its value from the logical place (href on A or action on FORM). Hx-boost does this to a limited extent, but I think it swaps out the body element from the response unless you specify otherwise, and modern browsers already do that quickly.
My main objection to these hx- atts is an aesthetic, nostalgic nitpick. I just don’t think they look good. I’d want the web page source code to look completely old school with just an include to make it JS-ified. I’d sacrifice the locality of behavior principle (and accept having to provide some separate sorta list of selectors for what part of the page is “hot”) to gain this retro chic. Sort of what I was going for with Along for the ride: plain vanilla HTML that includes a js file that makes it optionally special.
Help me. I am not a Ruby person and I probably never will be. I just cannot figure out what Hotwire is. I have read this post, I have read the Hotwire homepage, I have googled it, I cannot for the life of me figure out what it actually is.
I keep reading “HTML over the Wire” but that is how normal websites work. What is different?
you know how HTML is usually transferred over HTTP? well, Hotwire just transfers that same HTML over a different protocol named WebSockets.
that’s it. that’s the different.
Thanks. Your explanation saves me countless hours.
what on earth
For others who may be confused: This is dynamic HTML over web sockets. The idea is that the client and server are part of a single application which cooperate. Clients request chunks of HTML and use a small amount of (usually framework-provided) JS to swap it into the DOM, instead of requesting JSON data and re-rendering templates client-side. From the perspective of Rails, the advantage of this is that you don’t need to define an API – you can simply use Ruby/ActiveRecord to fetch and render data directly as you’d do for a non-interactive page. The disadvantage is that it’s less natural to express optimistic or client-side-only behaviors.
Ah. That … sort of makes sense, honestly?
it’s not really a new idea, they stole it from phoenix liveview. https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
… which I guess in turn is the spiritual successor to TurboLinks
It’s worth noting that idea isn’t new with Phoenix. Smalltalk’s Seaside web framework had this back in 2005 or so (powered by Scriptaculous), and WebObjects was heading down that path before Apple killed it.
Phoenix LiveView looks great, and is likely the most polished version of the concept I’ve seen, don’t get me wrong. But I don’t think DHH is “stealing” it from them, either.
There’s a lot of implementations of it, here’s a good list: https://github.com/dbohdan/liveviews
Not unsurprising, given that Phoenix is designed by a prolific Rails contributor. There’s a healthy exchange.
In addition to brandonbloom’s excellent points, I personally liken it to the Apple/Oxide push for hardware and software being signed together. This type of tech makes it much easier to keep frontend and backend designs coherent. It is technically possible to do with SPAs and the APIs they rely on but the SPA tech makes it too easy (at an organizational level) to lose track of the value of joint design. This tech lowers the cost of that joint design and adds friction to letting business processes throw dev teams in different directions.
Really? How is that the answer to all of life’s problems, the way DHH is carrying on?
because now your (data) -> html transformations all exist in one place, so you don’t have a server-side templating language and a client-side templating language that you then have to unify. also the performance of client-side rendering differs more substantially between devices, but whether that’s a concern depends on your project and its audience. just different strategies with different tradeoffs.
(data) -> html
Yes, these are good points; another is that you have basically no client-side state to keep track of, which seems to be the thing people have the most trouble with in SPAs.
Great for mail clients and web stores, the worst for browser games.
depends on the game. A game that runs entirely in the browser makes no sense as a Hotwire candidate. But a for a game that stores its state in the server’s memory it’s probably fine. Multiplayer games can’t trust the state in the client anyway.
If you genuinely need offline behavior, or are actually building a browser-based application (e.g., a game, or a photo editor, etc.), something like Hotwire/Liveview makes a great deal of sense.
At least until you get to a certain scale, at which point you probably don’t want to maintain a websocket if you can help it, and if you are, it’s probably specialized to notifications. By that time, you can also afford the headcount to maintain all of that. :)
So, long story short, I much prefer working with Alpine.js (or its knock off, petite-vue). It’s simple; it lets you sprinkle in dynamism while still using backend templates, but it doesn’t assume that you want to forget how to use JS and shove everything off to some framework which you don’t actually understand.
I’m in the same boat as you. I’ve switched to using Deno for my JS/TS backends. Deno’s gone out of its way to solve the overwhelming majority of the issues with Node, and I find the experience makes using JS/TS everywhere honestly one of the most pleasant environments I’ve worked in. YMMV, Deno’s stdlib is still a work-in-progress, etc., but it might be worth a look, based on what you said.
because while JS is a good language
because while JS is a good language
In what way do you think JS is a good language?
ES6+ is a really good and easy way to build frontends. It’s basically in the same category as PHP/Ruby/Python, so whether you like it will depend on how you feel about those languages, but comparing within the group:
Tastes vary, but if I’m going to write frontend code, it all ends up being JS in the end anyway, so it’s good to cut with the grain, and just use JS directly, since it’s a good enough language and adding intermediate layers just creates obfuscation when something goes wrong.
hm, curious if a hotwire connection has state on the server. What happens if you have a hotwire application that’s load balanced across N nodes and a client’s websocket terminates, then they reconnect, but land on a different server?
I’ve been working with a similar paradigm through Elixir + Phoenix + LiveView, and it works extremely well for my one-person side projects. I’m excited to see Hotwire become the default in Rails.
I wonder, though, does anyone have experience working with technologies like these on very large apps? I can imagine it’s challenging to scale it to larger teams – even for my small projects, I’ve found myself missing the concept of full-featured UI components.
I don’t think it’s quite there yet for UI components, but it is a very active area of development- see https://surface-ui.org/
My recent project was developing a new site, and I was using React to render static HTML on the backend, because I really like JSX + Typescript as an HTML templating language. I wanted some interactivity in a few places on the front-end. I played with Hotwire, and it worked, but in the end I felt “why am I using two technologies here” and also “I really wish I could write my logic once in my prefered framework (with Typescript!), render a proper static HTML page, and only add interactivity where it’s needed”.
Some Googling turned up that this is called “islands of interactivity” or (less romantically) “partial hydration”. It seems to be the current best-of-both-worlds. I can use React with Typescript and write code like I’m building a client-side app. But I actually ship static HTML that renders properly without JS enabled, and only the small parts that need JS are “hydrated” using the exact same code that rendered the static HTML. And this is transparent to me, and to the user.
There are a couple of frameworks doing this: Astro does it for static sites (I think SSR might in the works), and supports a variety of frameworks (React, Vue, SolidJS). Remix supports only React, but supports server-side rendering, not just static sites. The HTML shipped by Remix is fully-functional without JS, so forms will work, etc…
Note, this is different to React Server Components, which I’m a bit less sold on, but is the path being taken by Next (really by the React team, Next is just enabling it)
Personally I’m using Remix, which even in it’s just-hit-v1 state is pretty nice to work with.
Anyone see any benchmarks for Hotwire? Mention of performance is conspicuously absent from DHH’s article and the Hotwire home page. This other article idealizes performance by implying updates take only 18ms, but that’s not under realistic traffic conditions and doesn’t include the DOM update itself, only the HTTP overhead.
Generally speaking, the SPAs will perform better for anything but mostly static content, especially under heavy traffic. Hotwire sends requests to the server for every DOM update and wait for the server to render HTML. SPAs send requests to the server only as needed and perform DOM updates in the browser itself.
LiveView takes a similar approach. I hear it performs OK except for memory scaling issues. But being built on Erlang gives it the benefit of being designed from the ground up to manage stateful connections concurrently. I suspect Hotwire performs more like Blazor (i.e. not very well). It seems like it might actually perform worse under heavy traffic since Hotwire doesn’t compile application logic to wasm so it can be run client-side like Blazor does.
Looks like you skimmed the excellent post mortem there, liveview is used at scale already elsewhere. this is more of a pubsub design issue rather than an erlang vm or liveview issue per se. If you’re streaming updates faster than they can be consumed then you have problems anyway in any system. You need to find an alternative approach, which they did.
Used at scale where? I’d genuinely like to see some data.
The article I linked to was the first really in-depth one I’ve seen. I didn’t skim it, but it’s a fair point: If you want to provide live updates without refreshing, you’re going to have scaling challenges regardless.
12:00 on - but you can’t take this as a cargo cult example of success. They are using Liveview “at scale” but with what problems? ~19:00
I don’t know the exact details of where the friction happened.
Stress test already showed millions (2015) on a single server. https://fly.io/blog/how-we-got-to-liveview/
But I think this is micro/in-theory. I tweeted at Angel, I’m curious too.
I think a big part of the answer is that although in theory a carefully written SPA could out-perform HTML over the wire, other constraints take things very far away from optimal. Compare, for example, Jira vs Github issues (or a blast from the past like Trac, which is still around, for example Django’s bug tracker https://code.djangoproject.com/query). The latter two both feel much lighter and you spend far less time waiting, despite both being server rendered HTML.
Some of the reasons are:
Interesting. I didn’t realize Github used HTML over the wire. What’s their implementation? Hotwire? Something custom? I’m digging through their blog, but the only article I’ve found that’s remotely related is their transition from jQuery to the Web Components API, which only relates to relatively small interactions in widgets.
I’m working on a .NET project now that uses async partials in a similar manner, but user interactions are noticeably slower than a comparable app I’d previously written in Vue. The more dynamic content in the partial, the longer it takes the server to render it. There may be some performance optimizations I’m missing and I admit to being a relative novice to C#. But, in general, SPA bashing is rarely supported by evidence.
Let’s take your Jira example. I’m looking at a Lighthouse report and a performance flamegraph of the main issue page. To chalk their performance problems up to “client side join” doesn’t tell the whole story. It takes half a second just for the host page to load, never mind the content in it. They also made the unfortunate choice of implementing a 2.2 MB Markdown WYSIWYG editor in addition to another editor for their proprietary LWML. Github sensibly has only one LWML (GFM) and you have to click on a preview tab to see how it will be rendered. I think it’s fair to say that If you rewrote all of Jira’s features, it’d be a pig no matter how you did it.
Meant to reply to this earlier, then the weekend happened!
GitHub is basically a classic Ruby on Rail app - see https://github.blog/2019-09-09-running-github-on-rails-6-0/ and https://github.blog/2020-12-15-encapsulating-ruby-on-rails-views/ - using Web Components where they need it for enhanced UI. Open up web tools and you’ll see on most pages, the bulk of the page arrives as HTML from the server, and a few parts then load afterwards, also as HTML chunks. I’m guessing they have a custom JS implementation of this, it’s not that hard to do.
I completely agree that the comparison I made is far from the whole story, but part of my point is that other decisions and factors often dominate. Also, once you’ve gone down the SPA route, slapping in another chunk of JS for extra functionality is the path of least resistance, and justified on the basis of “they’ll only need to download this once”. While if you have HTML over the wire, where every page load has to stand on its own, I think you are more cautious about what you add.
I read that LiveView postmortem, and found it odd that none of the “lessons learned” included load testing (which AFAICT would have completely prevented the outage). Also, unbounded queues with no natural backpressure (aka Erlang mailboxes) are land mines waiting to explode.
I feel like I’m the only person who immediately thought of the old Hotline P2P protocol of the early 2000s when I saw the title.