← Back to context

Comment by yashasolutions

2 days ago

I have seen people rewrite entire application from React to htmx. It works. But the architecture required is a tad different. Also you need Alpine as a complementary library for the reactive parts. (I mean you could do a lot just with htmx but I find Alpine more convenient in many places when I need to work with json - since I don't control all backend and json isn't really a first class citizen of htmx)

The beauty of it is that you don't _need_ Alpine at all, Alpine just comes up because it's popular, it solves the problem of lightweight inline scripting, and it integrates relatively seamlessly with htmx.

If you don't want to use Alpine for whatever reason, you can just write your own javascript, you can use hyperscript, you can use some other inline scripting library.

Mr. HTMX touches on it in one of the essays: https://htmx.org/essays/hypermedia-friendly-scripting/

> when I need to work with json - since I don't control all backend and json isn't really a first class citizen of htmx

yeah, if you can't make the backend return HTML, you're in a worse off place if you want to use htmx.

There's extensions [1][2] for receiving and rendering JSON responses with htmx (though I haven't used them), but I totally understand it starting to feel like a worse fit.

1 - https://github.com/bigskysoftware/htmx-extensions/blob/main/...

2 - https://github.com/mariusGundersen/htmx-json

If you’re using Alpine already, then is there a good reason to use HTMX over alpine Ajax? They both look quite similar to me, but I don’t do enough front end work to tell the difference.

https://alpine-ajax.js.org/comparisons/

  • Htmx offers more flexibility than Alpine Ajax. Here's an example: htmx allows using relative selectors, which allow you to target elements relative to the triggering element in the DOM tree. This gives us a lot of power for swapping in pieces of UI without having to make up ids for lots of elements.

    I have a blog post in the works for this feature, here's a small code sample I made to show the idea:

        <div class="card"> ╾──────────────╮
          <header class="card-header">    |
            <a                            |
              class="button is-link"      |
              title="Load links for #167" |
              role="button"               |
              aria-expanded="false"       |
              href="/app/notes/167/links" |
              hx-trigger="click once"     |
              hx-boost="true"             |
              hx-push-url="false"         │
              hx-target="closest .card" ╾─╯
        ╭───╼ hx-swap="beforeend show:none"
        |   ><b>±</b></a>
        |   <a
        |     class="card-header-title"
        |     href="/app/notes/167"
        |     hx-boost="true"
        |     hx-target="#note"
        |     hx-swap="outerHTML transition:true show:window:top"
        |   >#167 Velificatio</a>
        | </header>
        ╰╼
        </div>

  • I have tried to use exclusively each of the libraries to better understand their limit, overtime I got to the following observations:

    - htmx is more straightforward (because a lot of the magic basically happening in the backend) and helps a lot to keep some sanity.

    - Alpine shines when you need more composition or reactivity in the frontend. But it gets verbose quickly. When you feel you are reimplementing the web, it means you went too far.

    For pagination, page structure, big tables, confirmation after post etc. I usually go with htmx. Modals, complex form composition (especially when you need to populate dropdowns from differents APIs), fancy animations, I prefer Alpine. (I probably could do that with htmx and wrapping it in a backend - but often more flexible in the frontend directly.)

    To me, the main reason why I use these libraries, is what I write today will still be valid in 5 years without having to re-write the whole thing, and it matters since I have to maintain most of what I write.

So, instead of using one JavaScript library with an entire ecosystem of tools that work together, you use two separate uncoordinated JavaScript libraries? Why do you think that's better?

  • Different libraries composing well together is the default assumption in most of software development. Only in Javascript have people given up on that and accepted that libraries don't work together unless they've been specifically designed, or at least given a compatibility layer, for the framework they're being used in.

    • Qt widgets don't work together with GTK widgets, and nobody considers this a crisis. I'm pretty sure you can't use Unreal engine stuff in Unity. GUIs require a lot of stuff to compose together seamlessly, and it's hard to do that in a universal way.

      HTMX achieves its composability by declining to have opinions about the hard parts. React's ecosystem exists because it abstracts client-side state synchronization, and that inherent complexity doesn't just disappear. When you still have to handle the impedance mismatch between "replace this HTML fragment" and "keep track of what the user is doing", you haven't escaped the complexity. You've just moved it to your server, and you've traded a proven, opinionated framework's solution for a bespoke one that you have to maintain yourself.

      If anything, the DOM being a shared substrate means JS frameworks are closer to interoperable than native GUI toolkits ever were. At least you can mount a React component and a Vue component in the same document. They're incompatible with each other because they're each managing local state, event handling, and rendering in an integrated way. However, you can still communicate between them using DOM events. An HTMX date picker may compose better, but that's just because it punts the integration to you.

  • > an entire ecosystem

    Ecosystems have their downsides too. Just a small example, no htmx users were impacted by the React Flight Protocol vulnerabilities. Many htmx users have no-build setups: no npm, no package.json, nothing. We don't have to worry about the security vulnerability treadmill and packages and tools arbitrarily breaking and no longer building after some time passes. We just drive the entire webapp from the backend, and it just works.

  • > instead of using one JavaScript library

    One never uses just one JS lib :) The JS ecosystem always comes with lost of tools, and libs, and bells, and whistles.

    I like Elm for this reason. Less choices. Zero runtime errors (I know it is possible in contrived examples, but I've seen and many teams have said the promise holds true after many years of using in production).