HTMX Newbie's Notes
Not sure which bubble I’m currently in now, but for some reasons I have heard a lot about HTMX lately. I’ve decided to build something with HTMX to see what it actually feels like in practice.
The project is a simple page that allows users to look up a word and quickly add it to Anki, a flashcard program. The current working prototype is https://vocab.tentativeknowledge.com. The adding note to Anki feature currently only works on desktop and depends on AnkiConnect add-on.
After user enters a word, the page makes a POST request to server. Server returns a HTML partial that contains pronunciations and definitions of the word. I simply use HTMX to inject the partial to a container element.
It’s very easy to experiment with HTMX on the client side. I only need to add a link to HTMX’s js code to the page and decorate the form with hx-post
and hx-target
attributes.
The server and client interaction is also made very simple. There isn’t a need to create a REST endpoint. The server already sends an HTML page to the client, sending a HTML partial is more or less the same. This significantly simplifies things in case I need to worry about things like authentication, CSRF tolken, or any other things that many established frameworks have provided OOB for a very long time.
However I quickly discover that I’m missing all the progress (debatable?) have been made in the frontend world for developing client side apps in the last decade.
I miss Typescript for static typechecking my js code. I have been using typescript since 2014, so writing in JS without any type annotations makes me feel .. naked and scared. So I’ve decided to add typescript to the project, which leads to setting up a client side build step. Ugh, I thought I could skip this step if I use HTMX ☹️. To be fair, it’s me who depends on TS, not HTMX.
I miss writing templates in JS and have all the nice toolings to keep me productive. Now I have to jump back and forth between HTML and JS and relying one element’s ids and classes to keep HTML and JS in sync. I’m so not used to this and made so many mistakes. I once updated the template and acidentally removed a class name that is used in JS to hook up events. Nothing yelled at me, compilers and web servers are all happy … ☹️.
Speaking of ids and classes, they are also used for stylings. I, in general, do not like having the same concept used for multiple purposes especially when they are very different. Now I find myself adding ids and classes to hook up business logic AND to style the page. I thought to myself CSS utility classes might make this more tollerable. I could use 3rd party CSS utilities such as tailwind for styling. My own class names are just for syncing HTML and JS. But I do not like Tailwind. If I want to depend on 3rd party CSS framework/library, I would just use something with batteries included like Bootstrap. I haven’t used Bootstrap for awhile and was plesantly surprised that BS v5 now has utility classes besides all the familiar components.
Now let’s add Bootstrap to the project. Since I’m already setting up Vite to compile TS code, all just add Bootstrap via npm.
Wait, looks like I’m rebuilding the complicated frontend tooling that I thought I wouldn’t need when using HTMX 😲
In the project, I need to add a button to each definition of the current word. These buttons will add the corresponding definitions to Anki on click. I quickly realize that it’s not as simple as in, say, React. I now have to think about how to attach the event listener to each button: do I attach this same handler to every button, or to their commmon ancestor ? Not a difficult question, but it’s something I don’t have to worry about when using React.
Another thing that I realized is that, in React, most of the time I only need to focus on the current component: how it interact with its parent and children. Component hierarchy is a powerful tool. Now, while staring at the JS file and trying to figure out what HTML element in the HTML file I’m working with, the whole HTML page becomes … flat. In React, if you want to mess with another component far way in the tree, you need to make some real effort to do it. Here I can point my JS to any element in the HTML file easily. Looking out from insde JS code, the HTML suddenly feels flat and scary.
Let’s go back to the add-to-anki button. There’s some gotchas here. I have to be very careful with when to hook up the events. In modern frontend framework, you just directly associate the element with the event handler, so you do not have to worry about element loading order. Using HTMX’s hx-target
, the list of definitions is injected to the page after a POST request. I made a mistake when I have the code that wire the event handlers loaded with the page. At that point in time, there’s no definition elements to wire the events with, so nothing works!
I had to use htmx.onLoad()
to listen to when the definition elements are loaded and start wiring up the event handlers. This seems like an undesireable side effect of having templates and related js codes scattered in multipe files. Now I see why the official HTMX doc promotes inline javascript.
Anyway, I could go on, but this post is getting too long already. I guess the conclusion is that for me, personally, if I use HTMX, I would still end up with setting up the frontend toolings. I’m actually not at all familiar with settings these up. I have been working on an established product in the same company for that last 4-5 years. All of the set up has been in place for a while and there’s a infra team who is in charge of the toolings. I however have benefited a lot from these toolings to be productive. I like them and I will invest time to learn and set them up myself if I need to, which I did in this project.