The Rise of Low-JS Web Development
Hey everyone, welcome back to the lab. In this video, we're going to be comparing HTMX versus Alpine.js and specifically answering the question: which one should you use for building your web app?
So, low-js tools like HTMX and Alpine are the future of the web. They allow us to build modern web apps without the bloat of popular SPA frameworks. These are things like React, Next.js, SvelteKit, Vue.js. You know, they're better than what came before, but they are not as great as they could be.
Now, I've been researching and experimenting with low-js technologies for the past year, eventually deciding to build my apps with HTMX and F# instead of SvelteKit. I found that the low-js approach is much simpler while providing all the power I need to build a modern web app.
Now, one of the things that took me a while to understand, and that I see a good amount of confusion about in the community, is how to choose between low-js tools, HTMX and Alpine, for building your own web apps. Some guides will use one, some will use the other, and then many will use both. And so in this post, I want to clarify the differences so you can make the best choice for your web app and start building and stop bike shedding.
The first thing I want to note is that HTMX and Alpine.js are complementary. So the decision between HTMX and Alpine.js is not an exclusive one. These are not competing tools offering the same functionality, though there is overlap in what each of them can do. So this isn't like a choice between React or Vue or Angular or Svelte, in which case they're kind of doing overlapping things in different ways, and so you're probably going to choose one to do most of your work. Here, there is some overlap, but often you can use them together, and often that's what you will want to do. And so they're complementary and they work well together because they focus on solving common but slightly different aspects of web app development.
And so this is why using both of them is sometimes more powerful than using just one. And another aspect that they have that is kind of fundamental to this new trend or pattern of libraries we're seeing that we're calling low-js, is that they're going to provide these functionalities with like minimal unnecessary baggage. And so some examples of this that you know we might have come to expect or to be the norm with a lot of these like bloated client-side single page application frameworks, low-js kind of takes a different approach.
And so there's no build steps. You can just include it from a CDN on your web page and you can just use it. So you don't have to worry about npm, you don't have to worry about setting up Vite or Turbo or Bun or whatever random new build tool JS comes out with. Just include it on the page, just use it, it works. There's very minimal JS, so we're using mostly inline attributes, and so what this allows us to do is we can basically build all the logic and stuff directly in our markup and just send it. So this could be a static page, it could be a a server-side rendered page, and it will just have the functionality in the markup. And so we don't have to write all these like crazy framework things that these SPAs are doing, we don't have to worry about all this other overhead. It's very simple to see when you look at the markup, this is what the thing is doing. There's not like some file from 100 files away that's being pulled in randomly and causing weird behavior.
And the next one is that they're very easy to compose. Because there's no build steps, because there's minimal JS, because the logic is in the markup, it's very easy to see how these aren't going to affect each other. You can sprinkle them in in the web page where you want. They only affect kind of that local markup, and so you can use a little bit of it and you don't have to infect your entire code base with that choice, which unfortunately you do mostly have to do with the single page application frameworks, because they all rely on Node and they all have a specific way of doing things. And if you don't play with the life cycle, it just doesn't work as well. But you can do that with these low-js tools.
And so these work well together, which is why you'll see just as many apps using both HTMX and Alpine as you will just Alpine or just HTMX. And often what you'll see is a few of these low-js tools will be used together. So it might be HTMX and Alpine, but you could swap in a different one for either of those that are kind of focused on specific aspect of web app development.
When to Use HTMX for Server Interactivity
Okay, so now that we've seen the similarities of these libraries, let's talk about how each is uniquely solving things, what each is uniquely good at in the web app development sphere. So you're going to want to use HTMX for server interactivity.
And basically, think about using HTMX when we often are thinking about using interactive islands. And so basically most web pages are mostly static, and then we've got some widgets on the page that are like doing stuff. This is where HTMX shines, and where it really shines is because it allows you to do partial page reloads. And so when you do this, you can easily make your page dynamic based on data that lives on the server. And most web apps really just display data that's on the server, and HTMX is really good at this. So for server interactivity, when you want to access data on the server, that is what HTMX excels at.
And this is my overview of interactive islands. This is from my guide, Using F# and HTMX for simple interactive islands, and you can check that out there.
Okay, so HTMX is focused on making server interactivity simple and powerful. Its unique trait is that it's hyperfocused on hypermedia controls. What this means for us is that it controls everything via HTML attributes, HTML being a hypermedia. You can tell HTML elements how they should behave when a user does something or an event occurs. So you can say like, if you click this button, send out this request to the server, or after 30 seconds, send out this request to the server. And so it'll send a request to the server based on, you know, you have a trigger here, it'll send a request to the server. You'll have an attribute saying what do you want me to send to the server, where does it go. And then you can then say this is what you do with the HTML that is returned. And what this allows us to do is swap out partial pages. And this is, this is really important. It's this partial page reload that gives this superpowers.
Now, this may not seem like much, but the ability to do partial page reloads, and in particular, targeted partial page reloads, is actually the fundamental thing that SPAs, the single page applications, are doing to feel fast. When we think about it, what is Svelte and SvelteKit doing? What is React and Next.js doing? What is Vue.js and Nuxt doing? What they're doing, the fundamental thing they're doing is that they have a backing data store, and they connect it to the UI that's rendered. And then when the data store changes, they are only partially reloading the UI. That is the fundamental value proposition that they bring, is that they're doing partial page reloads. And this makes it feel fast and it makes it feel dynamic. And so HTMX allows you to build these same modern feeling apps because it gives you the power to do these partial page reloads directly in your markup. But because it's in the markup without the extra baggage, this is simpler, faster, and cheaper to build.
The 3 Primary Use Cases for HTMX
Okay, so that's how it does that. So when should you use HTMX? And I think there's a few cases here, and these are basically 'ands'. So if any of these fails, HTMX might not be the best tool, at least for that one particular type. Again, it's highly composable, so you can use it in parts where you need it, and then if you have a special case, you can go use something else that's better suited.
The first one is when you have control over the server and its responses. So HTMX does expect HTML returns, which is different from a lot of libraries. A lot of APIs and stuff are going to try to return you JSON or whatever their, you know, RPC binary format is, but it does expect HTML to be returned. So what you're going to need to be able to do is modify the server endpoints, because if you want to change the HTML that's being returned, you want to change the UI, the controls, how it interacts with the server, you need to be able to change that HTML. You need control over the server. If you don't have control over the server for whatever reason, then HTMX is probably not going to work that well for you because you're going to lose that control.
The next time when you should use HTMX is when your app runs on data only the server knows about. Now any app that persists data or does CRUD operations does this, right? Like if you think about what most apps are doing, they're really just reading data from the back end and getting the data on the front end. They're trying to display the data, they're trying to allow users to do stuff on the data. So this is most apps, actually, even though this seems really, you know, simple. So most apps do this. But if your thing is only doing things that exist in the browser... these are things like JavaScript-based calculators or maybe you have like an image editor that is doing everything locally, maybe even like a video game that's all running locally and there's no like back-syncing, there's no UI syncing from the server. Basically, everything's rendered front-end, then HTMX probably won't help that much. So for most apps, like you know, they're just doing the server stuff, HTMX is great. But if it's just client-side stuff, like, HTMX doesn't really need to help you much because the server doesn't need to get involved in that stuff.
Okay, and then the next time when HTMX is good is when your app doesn't need complex or high-speed rendering. And so HTMX is quite fast, and in many cases, it's much faster than SPAs when data needs to be read and returned from the server because it skips a whole layer of logic for API needing to serialize to a specific form. And then you got to send it over the wire, and then the client needs to deserialize this thing, and then it needs to parse that data out to understand it, and then it's got to turn that data into something usable based on its own business logic to turn it into the actual UI. We kind of skip that whole step because a server just does it and just sends you a thing and it's like, 'use this'. So often for these like things where we need server data, HTMX is actually quite fast and faster than these other frameworks. But that doesn't mean that, you know, it's still a network request. This isn't for free. And so if you're building something that needs to run at like 60 FPS, that's doing a lot of like frame re-renders, like a video game or something like that, then like a network call to the server just will never be fast enough to kind of do the updates. And so you should really reach for like a more specialized local rendering engine, you know, whether it's like Phaser or I don't know, use like wasm or something so that's actually fast locally. That's what you're going to need to kind of support that very fast back and forth. But most apps don't need this, like unless you're building a video game, really, you won't need that. But in that case, you wouldn't want HTMX probably.
And so to conclude, you know, HTMX is great for building web apps that require server interactivity and where you're displaying and updating data located on the server, which again, is most web apps. And even for web apps that don't do this, a majority of the codebase is going to be doing this. So still very good for adding this kind of interactivity, sprinkling it in to your code base. And so for these cases, reach for HTMX. Great tool for this.
When to Use Alpine.js for Client Interactivity
Okay, so that's HTMX. And for Alpine, we want to use Alpine for client interactivity. And so while most interactivity, you know, probably 80-plus percent of what you might want to do in a web app, this works well with HTMX's SSR HTML approach, because again, most web apps basically just displaying and updating data on the server, it may not be the best tool for everything. So for example, there are some things we might want to build to make the user experience better that doesn't rely on or impact in any way the data that the server has.
And so these are going to be things like, you know, showing or hiding a modal. So like if I were to click this and it pops up a modal, and then I click something else and the modal disappears, like the server doesn't need to be involved in that because I'm not changing data, I'm not—I don't need to read dynamic data. It's just stuff that's on the web page already, and it's like, 'oh, I should show the user, I should get it out.' It's just it's augmenting the UI, but the server just doesn't need to be involved in that. It's not adding anything.
Another example of this is like if we're interacting with a JavaScript library on a web page, like, I don't know, if we're doing math or something. Maybe we have like a math calculator like JavaScript library and we want to say like, 'here's the inputs from the user, give me the thing back.' Like the server wouldn't add anything in this interaction because we have everything locally. Or maybe it's a map library. Like the map only exists in our client, so we don't need to ask the server about this, we just need to tell the JavaScript map library to like change or whatever.
For these things that only live on the client and they don't gain anything from asking the server for information or they don't need to update the data that exists on the server, then it doesn't really make sense to do that server network call. And in fact, trying to ask the server for this information or trying to get the server involved in this thing that's really only client-only can often be awkward because it's like, 'why are we doing this?' Like it's unnecessary and slow. It's just like a network call will never be as fast as like a local thing. So if you don't need to make a network call, if there's no need for that, then like don't. And so this is where Alpine really shines and why Alpine, or really any other kind of lightweight client interactivity library, is often used alongside HTMX. HTMX is really great for the server interactivity, but for client-side stuff when we don't need the server interactivity, you'll often use something else. Could be like Alpine, jQuery, hyperscript, things like that. So kind of get you the extra 20% of the way to improve your UX.
The 2 Primary Use Cases for Alpine.js
Now Alpine.js takes a similar low-js approach, allowing you to declare client-side state and functionality in a simple, concise way directly in your HTML. And this allows it to play very nice with HTMX, as all your minimal logic declarations can be declared and viewed all in one place, which kind of serves the locality of behavior principle. And so HTMX, you're just doing HTML attributes, it's really just right in the markup. And same with Alpine for the most part, it's all going to be directly in your markup. And then you have the option of adding a little bit of extra JavaScript that plugs into your Alpine if you want to do something a little bit more fancy.
Okay, so when should you use Alpine.js? The first one is when you have functionality that is client-side only, as we kind of talked about. You know, the most common examples of this is like showing or hiding a modal or maybe a dropdown, like a hamburger menu, stuff like that. This stuff really only exists on the client to augment the user experience, and the server doesn't need to know about this. And it doesn't really make sense to wait on a network call for it. So things like this where the server just, it doesn't care if it's open or closed, it only cares what like the end action is like, just, you know, don't ask the server for it, just do it on the client.
The next one is when you need to interact with JS libraries on the client. So you know, JavaScript has a massive ecosystem. It's likely you're going to want to leverage some third-party libraries for visualizations or drag-and-drops or you know, anything like that. And for these, like you might as well just use JavaScript for easier integration. You're going to have to do that anyway. You don't need to get into the 'wait on a network call to send you back something and then you run it.' Like Alpine gives you these simple life cycle hooks, you can run it directly on the client, you can use browser native events. So you know, don't make things hard on yourself. Just like, use the little JavaScript here. And so Alpine.js is really great for sprinkling client interactivity in your app with minimal JavaScript overhead. So when it doesn't make sense to ask the server something, Alpine.js can fill in the gaps.
Conclusion: The Power of the H-Stack
Next, so far I've really been enjoying building web apps with HTMX and Alpine. I found it allows me to build my web apps faster and cheaper than the SPAs ever did. Plus, it doesn't lock me into a specific technology like Node, so I can build full-stack apps with F#, my favorite language, and HTMX and Alpine for front-end interactivity. And this is the power of the H-stack: Hypermedia on whatever you like. And really what these low-JS Technologies unlock is that you don't get bound into this whole ecosystem to just like use this one thing. Which unfortunately, like, you know, React and Svelte and Angular and Vue say that you're not locked in, but you kind of have to run Node or something or some build tool in order to get it to work. So it's very unwieldy to use in any other kind of back-end. And that's why we end up with all these new full-stack things, which are Next.js, Nuxt, SvelteKit, and all those things, which are trying to make this easier. But again, it's like you're just making it more explicit that I am bound into this whole tech stack when I didn't want to use this anyway. All I wanted was just to make it easy to make front-end. So the power of the H-stack, this is why we like low-js.
Now if you like this post, you might also like 'Three areas I'm exploring to build more side projects faster and cheaper in 2024'. Low-js is one of these, also exploring some other things, really trying to make my development cycle just simpler and nicer and honestly better than kind of what's been popular for the past five years because it's just too complicated. We don't need that. The next one is 'Simple interactive islands with F# and HTMX', kind of giving an overview of how I'm thinking about building full-stack HTMX apps on my back end of choice. And then you might also be interested in 'Why I'm moving from SvelteKit to F#', which kind of goes through my journey of falling in and out of love with these SPA frameworks, and then kind of how I found something better and then decided that that was like the right path to go.
That's it for this video. Thanks for watching and I'll see you in the next one.