1. 46
  1. 25

    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♥

    Just JavaScript as a layer of polish instead of as a prerequisite.

    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.

    1. 9

      That example just needs a change to the example, not to HTMx. If you added hrefs to each of those A tags, they would work without JavaScript and wouldn’t confuse HTMx. You just need to either tell HTMx to pick out the tab element from the response to swap for the tab on your current page, or make your backend controller serving that page respond to htmx requests with just the tab element (ie a partial view).

      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).

      1. 8

        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.

        1. 3

          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.

          1. 5

            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.

    2. 22

      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?

      1. 45

        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.

        1. 9

          Thanks. Your explanation saves me countless hours.

          1. 9

            what on earth

            1. 28

              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.

              1. 1

                Ah. That … sort of makes sense, honestly?

                1. 7

                  it’s not really a new idea, they stole it from phoenix liveview. https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html

                  1. 6

                    … which I guess in turn is the spiritual successor to TurboLinks

                    1. 5

                      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.

                      1. 5

                        There’s a lot of implementations of it, here’s a good list: https://github.com/dbohdan/liveviews

                        1. 4

                          Not unsurprising, given that Phoenix is designed by a prolific Rails contributor. There’s a healthy exchange.

                          https://contributors.rubyonrails.org/contributors/jose-valim/commits

                          1. 5

                            Moreover, DHH has been experimenting with these techniques since roughly the same time that Elixir (not even Phoenix) first appeared: https://signalvnoise.com/posts/3697-server-generated-javascript-responses

                        2. 1

                          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.

                    2. 4

                      Right. These days, a lot of normal websites transfers JSON over WebSockers and piece the HTML together on the client side with JavaScript, and Hotwire is a reaction against that, bringing it back to transfering HTML.

                      1. 3

                        Really? How is that the answer to all of life’s problems, the way DHH is carrying on?

                        1. 10

                          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.

                          1. 4

                            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.

                            1. 1

                              Great for mail clients and web stores, the worst for browser games.

                              1. 1

                                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.

                                1. 1

                                  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. :)

                      2. 9

                        The time is right for Not React, but I think Hotwire is a poor candidate for the new Not React. It’s clearly written by people who hate JavaScript and just use it grudgingly. That’s fine for them, I guess, but I actually like JS. It’s one of my favorite languages. I just don’t want to use it on the backend, because while JS is a good language, Node is a bad ecosystem. (“Node is the new IE.”)

                        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.

                        1. 4

                          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.

                          1. 2

                            because while JS is a good language

                            In what way do you think JS is a good language?

                            1. 7

                              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:

                              • It’s more consistent and logical than PHP, which is extremely haphazard in its assemblage (“design” is too strong a word here).
                              • It’s less wild than Ruby, where people are constantly reaching into the internals to create magical DSLs that are impossible to understand as a new dev.
                              • It has better async than Python and makes better use of anonymous functions.

                              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.

                          2. 9

                            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?

                            1. 7

                              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.

                              1. 2

                                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/

                              2. 7

                                I played with Hotwire, and I think the time is only right for Hotwire if you already have a static HTML site that you want to sprinkle some JS onto, or your backend is not written in Javascript (because if you are using JS on your backend you should look at whether your framework can render both on the server and the client).

                                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.

                                1. 3

                                  I used Intercooler.js on a medium sized project, and now I’m trying out its successor HTMx. Eventually, I guess I’ll need to do a comparison with Hotwire. The impression I get is that with HTMx you can go farther before having to drop down to JavaScript, but it’s also relatively more low-level and fiddly.

                                  1. 2

                                    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.

                                    1. 2

                                      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.

                                      1. 1

                                        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.

                                        1. 1

                                          https://smartlogic.io/podcast/elixir-wizards/s7e2-jose/ 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.

                                      2. 1

                                        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.

                                        Another example would be my current client’s custom admin SPA (Django backend) . Some pages are very slow for a whole bunch of reasons. I reimplemented a significant fraction using the Django admin (server rendered HTML, almost no javascript), in a tiny fraction of the development time and the result felt 10 times lighter. Unfortunately this project is too far gone to change track now.

                                        Some of the reasons are:

                                        • app structure means you do far more HTTP requests, and then effectively do a client side join of data that could have been processed on the server.
                                        • js libraries or components encourage loading all data so you can sort tables client side, for example, slowing down page load
                                        • browsers are really good at rendering HTML fast, and SPAs end up downgrading this performance.
                                        • visibility and optimisation of server side rendering performance is massively simpler.
                                        • a single dev is typically responsible for a page loading speed, as opposed to split frontend/backend teams, which makes a massive difference.
                                        1. 1

                                          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.

                                          1. 2

                                            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 can’t comment on .NET technologies. I also agree that there are times when you simply must have the low latency of Javascript in the browser with async HTTP. I have an app exactly like this - one page is very demanding on the UI front, and complex too. It’s currently about 7000 lines of Elm, but I imagine that React would have probably worked OK too. But it would be terrible, both in terms of complexity and performance, with server-rendered HTML. But in my experience quite a lot of apps really don’t need the SPA. For that app, I just have the one page that is SPA-style (and it’s a critical page, user’s will spend 80% of their time), but the rest of the site, which contains a long tail of important functionality, is server-side HTML with some smatterings of JS and HTMX.

                                        2. 1

                                          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.

                                        3. 1

                                          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.