DEV Community

Robodobdob
Robodobdob

Posted on

HTMX - Rethinking the SPA

About me

Hello, my name is Rob King.

I have been a full-stack developer for over 25 years here in Wellington. The majority of my career has been in the .Net but I have dabbled in various backend and frontend framework in that time. I’ve worked across various industries from education, legal, government, automotive, and healthcare.

Lunit

I am currently an engineering lead in the Cancer Screening Group of Lunit. We used to be Volpara Health when I joined and then we were became part of South Korean company Lunit. We are a health technology company which provides a AI products for the screening and diagnosis of cancer.

While I am not an actual time traveler, I once heard software development described as being a time traveler…. Not only do you build for the future, you often also work
the past.

SoundsNZ

Let’s go back to the year 2003 and visit SoundsNZ.com. I worked on this site around 1999/2000 but this screenshot from archive.org is largely the same as what I worked on.

SoundsNZ was New Zealand’s first online music retailer. It was very successful at the time and they did a lot of business through it.

Architecture

Architecturally, it was a classic, server-side Active Server Pages application with a Microsoft SQL database. Because this was before “ the cloud” existed, we hosted and ran the site on an IBM server in Wellington, unceremoniously in a small room opposite the office kitchen and toilet facilities.

Classic ASP was a purely server-side Mutliple Page Application architecture which was the ONLY way to build dynamic web applications - dynamic meaning, the content rendered on screen could be different depending on various conditions or data.

Server Side Rendering

Because it was server-side, every interaction was limited to one of two things - changing the page by clicking a link or changing some data by submitting a form. This was not a limitation of the MPA architecture, but really a limitation of HTML itself.

JavaScript

Because we were a hip and modern web design company, we did use Javascript around the site for small uses of interactivity like a select-all / deselect-all for example.

But Javascript was very much a small part of the main application which was firmly entrenched in server-side rendered pages.

Enter the SPA

Then, sometime around the 2010’s we started to get a new architecture to developer web apps with. It was called the Single Page Application (SPA) and it was offered as a solution to the issues with MPAs at the time. MPAs suffered from a confluence of the prevailing internet conditions at the time, as well as patterns that could be be avoided due to browser deficiencies.

The promise

The fundamental promise of the SPA was that we could download the frontend portion of our application to the user’s web browser and let it handle the heavy-lifting of assembling and rendering our UI. This would leave the server to only be concerned with sending data to and receiving data from the frontend in the JSON format.

In a time when internet speeds weren’t great and shifting and entire DOM + assets to the browser every time the user clicked a link, just taking that hit once and then shifting lightweight JSON over the wire was a VERY attractive prospect. Not only did it reduce traffic, it made apps feel faster and more interactive because we avoided the problems of traditional server-side apps such as the dreaded white screen refresh.

Growth of the SPA

As the SPA took hold and became the de facto way web applications were built, we started to replace bespoke implementations of the SPA architecture with consistent, and dependable frameworks such as Backbone, KnockoutJS, React, Vue, Svelte, Angular + many, many, more.

Where the traditional MPA was a thin-client network
application, the SPA swung the pendulum back towards the thick-client.

Problems with SPAs

However, it wasn’t without its issues. Developers now needed to effectively manage a larger application across two machines - the server where the code ran to manage
the state and the client where the code had to manage the user.

This split personality application became worse particularly if the development team were used to a certain
server-side language or platform and then they quickly had to get good at Javascript.

They also had to now understand new things like JWT tokens and API endpoints. They had to understand REST (I will cover this specific term a bit later). And above all
else, they had to pick a framework that suited them and hope it was the right choice.

A return to SSR

Now, this all happened in the 2010-2020 timeframe. But interestingly, there’s been a noticeable shift in the web development industry back towards server-side rendering.

It is not a complete swing back, but an equilibrium is being found which balances the benefits of the MPA (server-rendering is usually must faster to render HTML) and the benefits of the SPA (an action has almost instant feedback).

In fact, some frontend frameworks now tout their server-side offerings as the START point to their frameworks. For example, I have been using Blazor since .Net 5. The Blazor Server architecture is founded very much on “the server has first shot at rendering the markup, and we’ll send the SPA bits next”. Likewise, React Server Side Components looks to be a similar pattern - send what the server rendered first (because it’s faster) and then bring the interactivity in.

HTMX has entered the chat

The pendulum swinging to the middle conveniently bring us to HTMX. I believe HTMX lies at a confluence of server-side speed / client-side interactivity / developer experience.

HTMX is founded on the fundamental question of why can’t any HTML element trigger an HTTP request or receive an HTML response. There is a more expanded version of the https://htmx.org website, but I feel this is the crux of it.

The premise of HTMX

The fundamental idea is HTMX lets the browser do what is native to it (render HTML and handle user events), lets the server do what it is good at (manage state and build HTML).

Given that HTML out of the box is actually quite limited in what it can do, HTMX seeks to introduce the missing bits of HTML that make it truly useful as the “engine of application state” aka HATEOAS. While the state is technically persisted on the server, the hypermedia is how you interact and manipulate it.

  • A database table can be displayed as a table in HTML.
  • A button in that table is clicked to remove the row which causes state to change on the server.
  • The database table is re-displayed with the updated state

Library, not a framework

Technically, HTMX is a library and that means there are no dependency graphs or NPM packages or node_modules to worry about. Simply add a CDN or local reference and you have HTMX-ready application.

It’s also a tiny library, so there are zero concerns about burdening your user with extra load. All the user will get in their browser is the assembled HTML and any supporting assets (HTMX, CSS, additional JS etc.).

Locality of Behaviour

Another fundamental of HTMX is the Locality of Behaviour which distills to “The behaviour of a unit of code should be as obvious as possible by looking only at that unit of code”.

In practice this means you are adding HTMX directives to your markup directly as hx-* attributes. While this may feel like it pollutes markup, it actually makes reasoning on the markup far simpler for a develop to get up to speed with.

If you look at these examples, while we can infer that getTime() is going to get the time, we don’t know how or where it will do it. We also don’t know where this actual function lives in our codebase.

<button onClick=”getTime()”>Get the time</button>
Enter fullscreen mode Exit fullscreen mode

But, if we take an HTMX approach where we have a time endpoint, we can see that when clicked we are going to hit the endpoint and get a response back.

<button hx-get=”/time”>Get the time</button>
Enter fullscreen mode Exit fullscreen mode

Progressive enhancement

Adding HTMX to your application does not mean you are now building an HTMX application. It will happily apply small islands of interactivity as required.

At its most basic usage, you can even just use the hx-boost attribute at a high-level in your DOM. This will give an immediate benefit by using intercepting your normal HTML interactions and then patching the DOM with the response when it comes back.

Demo code

Head over to https://github.com/robodobdob/NoteToSelfHono and clone the repo to have a play with the demo site. While I am a .Net developer, this application is actually built on the Bun/Hono stack. The README should you get started.

You will need to set up a database and storage account. You can also just browse the code and I’m sure you will be able to understand how HTMX works.

The basics

Fundamentally, pay attention to hx-* actions and hx-target attributes. They will tell where the markup in being generated and where it is ending up, respectively.

Also, look at the browser dev console and watch the network tab as your interact with the application. You will see that interactions only trigger the bare minimum of network traffic. The only things sent over the wire will be requests to HTML and responses with just the HTML you need. There are no reloads of static assets or the entire DOM tree.

Swaps

In the dev console, pay attention to the DOM elements because you see flashes of the which elements are changing from the response. HTMX patches your DOM with the response depending on the hx-target attribute.

You will sometimes notice multiple DOM elements update from a single request. This is call an out-of-band swap and these are done using the hx-partial tag. These are fragments that can be added to a response which will be stripped off by HTMX and rendered somewhere else in the DOM. This is especially useful if you have multiple updates that need to apply at the same time.

Headers

It’s worth looking at the request and response headers too. HTMX will add request headers by default which can be useful for determining aspects of the request such as which element fired it. You may want to return different HTML depending on which element as trigger, for instance. Response headers can also be used to trigger HTMX events or even plain Javascript event handlers.

The story so far

In my experience so far, I believe HTMX provides a best-of-both worlds to web development. You get the benefits or server-built HTML but with the fast interactivity and DOM changes that users expect from a SPA.

In defence of SSR

I think it’s also worth covering that just because something is using server-side, it is inherently slow or old-fashioned or expensive. Servers and cloud infrastructure are fast and cheap now.

Gone are the days of the multi-thousand $$$$ server in the corner of the office. We can now stand up scalable cloud infrastructure for $/month that can serve many, many users.

This belief of also based on the idea that your application is serving a public audience with a variable user base. There will be thousands upon thousands of internal web tools , that run big business globally, that will never experience the white-hot load of “Slashdotting”. They will be the tools that are built to service a specific need in that organisation and will likely have a known user base.

It is also important to consider what the application itself is actually doing. If you are building a highly interactive, realtime application like Figma, then HTMX is not for you. But, if you are buidling another forms-over-data web application like the thousands of other developers, then HTMX is likely to fit the bill.

This only scratches the surface of what HTMX is and does, so I highly recommend reading the documentation at https://htmx.org to get a sense of breadth of functionality on offer.

Quotes

In preparing this talk, I gathered various quotes from around the web about HTMX. These were all unprompted and just things I had seen in passing.

Of particular interest is the bottom quote which is from an article entitled Building Critical Infrastructure with htmx: Network Automation for the Paris 2024 Olympics. It is well worth a read because it shows that HTMX is a solid choice for large, important applications.

Useful links

Here are some other useful links which will hopefully interest you more into the world of HTMX.

Finally, this a translation of a post which I found. It’s perhaps a bit extreme, but HTMX does engender strong feelings :-)

Top comments (0)