DEV Community

Cover image for HTMX, AlpineJS, SSR v1/2/3 and SPA | Easy missing guide.
Emil Valeev
Emil Valeev

Posted on

HTMX, AlpineJS, SSR v1/2/3 and SPA | Easy missing guide.

Preface

HTMX and AlpineJS are two JS libraries that extend HTML with special attributes and allow you to build frontend interfaces without SPA frameworks.

They are alternatives to React/Vue/Angular and the like. Their emergence is a response to fatigue from the complex JS ecosystem and nostalgia for the times when problems were solved with PHP and jQuery.

All my life I’ve written JSON APIs and SPA interfaces, and my brain struggled to understand why these libraries are needed and how to use them. That's why I wrote this article.

HTMX vs AlpineJS — What Do They Handle?

These two libraries have some overlap in functionality, but each generally solves its own problem.

  • HTMX: For making AJAX requests to the server, receiving HTML, and dynamically updating parts of the page.
  • AlpineJS: For logic tied to local client state. All sorts of isVisible flags, conditional rendering, etc. In short, anything that exists only on the client. Another way to think about it — HTMX connects the frontend with the database (through the server API), and AlpineJS is a convenient way to have state on the frontend and render the UI dynamically from it.

Server Side Rendering vs SPA

Disclaimer: Terms v1 and v2 are my own.

SSR v1 (Web 2.0, PHP era)

Classic server-side rendering existed before the term itself became popular, simply because there was no other kind of rendering.

This is when the client makes a request to the server, the server returns an HTML document, and the client (browser) fully redraws the entire page. The user experiences a "reload".

This was typical for Web 2.0 era sites, and such UX is no longer considered high quality because:

  • It's not responsive enough.
  • It doesn't support local state — all state is stored on the server. Anything you entered in a form, any menus you opened, all will be reset after a page reload.

SPA (Single Page Applications, ReactJS era)

As a response to the problems of SSR v1 and Web 2.0, SPAs (Single Page Apps) appeared.

This is a confusing name because, from the user's perspective, a site/app can have as many pages as needed.

"Single page" here is in a purely technical sense. It means that now we have a single HTML document (page) and the entire UI is rendered dynamically via JavaScript.

This solved the two problems above but created a bunch of new ones. There was now a lot more logic on the client, but the technologies for this were not ready. JavaScript was designed for writing small scripts and didn’t scale well as codebases grew.

In response, other technologies started to appear, such as TypeScript and bundlers like Webpack (and modern alternatives), linters, test runners, package managers, etc. All of this infrastructure was fragmented, with no official support from language authors (unlike Go), requiring developers to maintain it themselves.

This led to the situation where the person responsible for the frontend also had to handle “build” tooling. There are a lot of tools to master and configure, and some of them periodically break backwards compatibility.

In short, frontend became very complex. And this complexity wasn’t from the tasks themselves, but from the low quality of the tools, which developers had to fight endlessly instead of just solving business problems.

Add to this the fact that in the JS world, every weekend a new framework pops up, and the ecosystem is constantly shifting and changing.

All this led to “JS fatigue” (c).

SSR v2 (NextJS era)

I won’t call it Web 3.0 since that term is more about decentralization.

Server-side rendering v2 wasn’t about JS fatigue, but about solving a technical problem — search engine indexing.

When SPAs appeared, search crawlers weren’t adapted for such sites. They still expected the server to return static HTML, but instead got HTML with nothing in it, requiring JavaScript execution to render content.

Modern crawlers can handle this, but server-side rendering is still preferred for SEO. Search indexing is complex, with many nuances. Let’s just accept the fact that:

SEO <3 SSR

So, on one hand, we want dynamic SPA interfaces, on the other we want static HTMX for SEO, as if we had SSR. What to do?

The industry made a full circle back to server-side rendering, but now using different technologies.

Previously, a PHP server would query the database, generate HTML via a templater, and return it to the client. Now, a NodeJS server runs JS inside the HTML page to generate static HTML and send it to the client. This way, we could keep using SPA frameworks like React and have lots of client-side logic, but the initial HTML received by the browser wasn't empty — it had meaningful content for indexing.

HTMX and AlpineJS as a Cure for JS Fatigue

Technically, the latest evolution solved all problems except one — there’s still too much JavaScript, and it’s still unpleasant and complex to write. “Unpleasant and complex,” by the way, isn’t just a vibe, it’s direct business damage. As your codebase grows, it becomes harder to change, so development gets more expensive. Fatigue converts directly into money because both are about energy.

HTMX as SSR v3

Disclaimer: SSR v3 is my own term.

HTMX philosophy:

  • Modern frontend is often unjustifiably complex. We should try to return to the origins of server-side rendering.
  • In the early web, there was the idea of Hypermedia — linking documents via hyperlinks, and the browser as the engine loading them. Think tags like <a>, <form>, <script>, <style>, <img> that could make requests without JavaScript. Unfortunately, the modern web moved away from this and became JS-heavy.
  • Returning to pure SSR v1 is impossible, because the modern world really does require more responsive interfaces. The compromise invented was fragments — let the server return HTML as before, but not the whole page, just its “fragment,” which JavaScript can insert/replace in-place. As a result, only part of the UI is rerendered for the user, as if it were an SPA. At the same time, it’s true server-side rendering, with all its advantages.
  • This led to a JS library that extends (HTMX = HTML eXtended) standard HTML with a small set of extra attributes, making any tags hypermedia, not just forms and links.

HTMX vs SPA — A Paradigm Shift

By now, it should be clear, but I’ll spell it out, as my SPA-optimized brain didn’t grasp it at first.

HTMX and SPA (React and others) imply different approaches to application architecture.

  • SPA: The server returns structured data (usually JSON), and the client renders HTML based on that data.
  • HTMX: The server returns HTML, and the client just inserts/replaces this HTML on the current page.

That is, rendering logic moves from client to server.

Technically, we have a set of HTTP endpoints returning HTML snippets and our client, which calls these endpoints in response to browser events (scroll to element, button click, etc.).

Important: There is no client state! Only server state.

We move away from client SPA libraries like React, with their branching, loops, etc. All this logic goes back to the templater, just like the good old days!

And we don’t write any JavaScript at all — only HTML with a few extra attributes.

Important: HTMX is not a silver bullet. I’ll repeat — there’s no silver bullet. This approach has downsides and may not suit you. More on that later.

AlpineJS as the Missing Piece

HTMX does nothing to help with client-side state. If you need some purely frontend logic (even something as simple as disabling a button under a custom condition), you’ll need to write JavaScript.

You have three choices:

  1. Go back to SPA (and maybe SSR v2), with all that entails.
  2. Write vanilla JavaScript, no libraries.
  3. Add a library — but not a full-blown SPA framework with build step, just something simpler.

AlpineJS fits exactly this niche. It brings back client-side state and lets you code frontend logic. Now you can disable that button under your custom condition, with branching, loops, etc., like you’re used to in React.

It looks more like Vue than React, though. Alpine extends HTML with a few extra attributes, like HTMX, but with Alpine, you do write a little JavaScript (a lot less than going vanilla).

Yes, native JavaScript is actually very low-level, as odd as that sounds. Its DOM API is imperative and verbose, and for many basic things needed in every project, you’d have to hand-code from scratch. It’s not like Go’s standard library, where the devs sat down and thought about the real-wo

HTMX + AlpineJS vs SPA. Downsides of SSR v3

If you compare this approach to classic SPA (or SPA + SSR on something like Next), it might seem like this approach is all pros. You get the best of both worlds and don’t pull Node into your project. Of course, that’s not the case. There are situations where this stack isn’t suitable, and you’re better off with React or Next.

  • If you have a lot of complex client-side logic that can’t be moved to the server. A grotesque example is Figma. That’s way more of an “application” than a website. Extremely high interactivity and a need for low latency.
  • Also, HTMX and AlpineJS don’t provide static typing like TypeScript does. If you want full type coverage between client and server, this stack isn’t for you. You may have typing at the template level (e.g., Templ if you code in Go), but from HTMX you can, for example, make requests to non-existent endpoints, reference non-existent DOM nodes, etc. Maybe there’s a solution in the community, but it’s not out of the box, so it won’t be as minimalist. Why you might want full typing is another question; it’s usually about codebase/team size. With AlpineJS, it’s exactly the same.

If you like this article, support my open-source projects:

Top comments (0)