Jayshua

Jayshua

HTMX - A js library sort of like liveview

I recently came across the javascript library htmx. It reminded me a lot of liveview so I thought the community here might be interested.

It basically lets you use HTML attributes to connect portions of your page to the server dynamically like this:

<button hx-post="/submit-button" hx-swap="outerHTML">Click Me</button>

Will trigger a post request to /submit-button and replace the button with the HTML in the response.

<div hx-get="/news" trigger="every 2s"></div>

Will replace the contents of the div with the HTML returned by a get to /news every 2 seconds.

It does websockets and server sent events too:

  <div hx-ws="connect wss:/chatroom">
    <div id="chat_room"></div>
    <form hx-ws="send:submit">
        <input name="chat_message">
    </form>
  </div>

The server can respond with HTML having the id="chat_room" hx-swap-oob="true" attributes which will tell htmx to swap out the #chat_room element with the new HTML.


The part I find most interesting is that there are no special requirements for the server at all. Not even for the websocket support. Any server which can reply with HTML will work.

Most Liked

100phlecs

100phlecs

I am using it currently.

Some things to watch out for:

  1. It can be barebones at time compared to other solutions (i.e. LiveView / Turbo)
  2. You’ll want to rewrite 302s to 303s to avoid browser issues when implementing DELETE. Another option is to set the HX-Redirect header when it is a 302(?), which I have not tried. Here is more context. Hotwired/Turbo does the same thing. To make the mental model easier (even if Not Proper™) I just rewrite all 302s to 303s when sent to the browser so I don’t have to reason about whether or not it’s the proper response status. This will affect your browser tests though, so it would be worth looking into the HX-Redirect alternative:
defmodule HTMXRedirect do
  import Plug.Conn


  def init(default), do: default

  def call(conn, _opts) do
    register_before_send(conn, fn conn ->
      if conn.status == 302 do
        put_status(conn, :see_other)
      else
        conn
      end
    end)
  end
end

Some issues I’ve ran into:

  1. Scroll position is not properly restored when the current page you’re on doesn’t have the same height as the previous page.
  2. Sometimes dynamically loaded scripts in the body do not load. I just disable hx-boost to those pages.
  3. Browser link resolution can cause the base url to point to the previous page.

bonus:

I use hx-preserve and create a container for topbar so I can get the same loading bar that phoenix has.

  1. "topbar": "github:100phlecs/topbar" in package.json deps, npm i
    ( GitHub - 100phlecs/topbar: Tiny & beautiful site-wide progress indicator · GitHub )
  2. in your app.js
import topbar from "topbar"

topbar.config({
  containerId: "topbar-container",
})

window.addEventListener("htmx:before-request", (_info) => topbar.show(500))
window.addEventListener("htmx:after-request", (_info) => topbar.hide())
  1. in your root.html.heex
    <div id="topbar-container" hx-preserve="true" />

Conclusion

Overall, it’s fine.
It’s rough around the edges, maybe optimized for Django? No clue.

I like how everything is an attribute, while in Turbo you have to create custom elements and massage your markup differently.
Being able to html-swap different parts of the screen helps (flashes, for example)

I did have to reinvent the wheel for other things, like asset digest mismatch (i.e. turbo-track-reload).

Let me know if you have any questions.

sreyansjain

sreyansjain

I would disagree to it. HTMX’s predecessor intercoolerjs is a lot older than Liveview.
So i don’t think its gonna disappear in some time.
There are many libraries with HTML over the wire idea like - Turbo, Unpoly, AlpineAjax etc.

I have used htmx and turbo for more than 2 years and liveview for around 4. My experience using them is as follows-

htmx:

  • Follows the http protocol and the server needs to return html that would get inserted in the right place.
  • Since it follows the http protocol you would need to create a lot of routes for calling server actions
  • Need to be mindful of updating multiple places in the same page. There is a probability of missing things if you are not careful (although such scenarios are less frequent).
  • Performance is slightly less than liveview but its not noticeable to the end users (non technical)
  • JS integration is easier than with liveview - no hooks are needed (but we need to be a little mindful about reloads).
  • No state on the server side, all state is in the client or the url
  • If you have simple pages (for ex a static website) just putting hx-boost would make it a lost snappier.

Liveview:

  • Based on web sockets
  • The client and the server become one and the network gap dissolves so we can call the server functions from client without defining additional routes.
  • No need to return html fragments, liveview would automatically figure out what to send based on state changes. This is advantageous especially when multiple places in the page needs to be updated. No extra care is required.
  • Allows better locality of behavior for html and code as they can be in the same file. But inferior locality of behaviour for JS as it needs to be in hooks in a separate file.
  • JS integration via hooks - slightly more complex than using vanilla js as we need to be mindful of liveview life cycle events. But you get used to it pretty soon.
  • If you have simple static pages - liveview is not a good fit.

These are my 2 cents.

garrison

garrison

There is really only one difference between LiveView and HTMX: LiveView is stateful and HTMX is not. Everything else (like the much more thorough list above) is directly downstream of that.

Honestly the differences between what LiveView is (a stateful app on the server patching a thin client) and what HTMX is (stateless XHR into DOM) are substantial enough that I don’t really see how they can be compared at all. This is not to say that one is better than the other - they just do completely different things. The programming model is wildly different, and so are the use cases.

The only other thing to note is that what LiveView does is really only possible on the BEAM, as it can actually handle that many stateful connections without catching fire. The HTMX approach on the other hand is something you can do on any stack (including Phoenix!). But again, they are completely different approaches that are not useful for solving the same problems.

Where Next?

Popular in Discussions Top

cvkmohan
The upcoming Phoenix 1.6 release looks very interesting. Became a habit to watch the commits - and - what they are bringing in. phx.gen...
New
pillaiindu
In django there is a cache framework backed by memcached. Rails also puts a lot of emphasis on caching, and even the idea of russian-doll...
New
mmport80
I have put far too much effort into Dialyzer over the last year or so - and basically - I doubt it’s worth the effort. It’s not as easy ...
New
crabonature
I’m still quite new to Elixir. As I understand we got in Elixir “multi guards” as convention to simplify one large guard with or’s?: de...
New
nburkley
AWS re:Invent is on at the moment with some interesting announcements. One new feature in particular is the Lambda Runtime API for AWS La...
New
ejpcmac
I have discovered Nix last month and I am currently on my way to migrating to it—both on macOS at home and the full NixOS distrubution at...
New
IVR
Hi all, I’ve seen a number of related threads in the past, but I’d still be very curious to hear an up-to-date opinion on this topic. I...
New
PragTob
Hey everyone, this has been on my mind for some time and I’d love your input on it! TLDR: I feel like maps are superioer for storing and...
New
Qqwy
I would like to spark a discussion about the static access operator: .. For whom does not know: it is used in Elixir to access fields of...
New
Markusxmr
Since Drab has been developed for a while in the open, introducing the Liveview functionality in a way it happend appears to undermine th...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement