How to style HTMX apps with CSS

Date: 2024-04-10 | create | tech | htmx | css | web-development |

HTMX is changing the way we build web apps. This makes sense - HTMX allows traditional Multi-Page Apps to achieve Single-Page App-like experiences with little added complexity.

But it's a relatively new paradigm so there's often no paved road for doing common things. Recently I've seen a lot of questions about how to style HTMX apps with CSS so in this post I'm going to share how I do this in production.

HTMX App Building Philosophy

First we need to start with the philosophy / values of HTMX which will help inform how we build styles into our apps.

The power of HTMX is that we can build SPA-like experiences with the simplicity of MPAs. Many are tempted to leverage and extend HTMX's partial page renders to do all sorts of dynamic loading of assets (JS, CSS, etc).

I believe this is the wrong approach. It adds a lot of complexity for very little gain.

Instead I think we should start HTMX apps by building them like a standard MPA. This gives us a core structure that does what we want - if a bit stiffly. Then we can sprinkle in HTMX to build Interactive Islands for those parts of the page that would benefit from added dynamism.

Usually there will only be a few areas on a given page that would actually benefit from this dynamism so this gives us the power we want with minimal added complexity.

CSS in HTMX

So how do we apply this approach to CSS? MPAs (and most websites really) tend to have one large CSS file that they use for the entire site. They simply plop this thing in the header and every page gets access to all the available styles.

This may seem a bit blunt and inefficient:

  • what if there's a bunch of unused styles?! - wasted memory, slower speeds
  • What if the payload is super large?! - slower load speeds

These are valid concerns. But largely this doesn't really matter til you're at enterprise scale and at that point you can worry about it.

For most apps outside of FAANG that isn't doing egregiously large stylesheets, this works very well and has many benefits:

  • Easy to setup
  • Can be cached (same CSS on all pages)
  • Allows HTMX to hx-boost a page (aka not reload it at all) - this only changes the body so if you're doing dynamic CSS styles this would break. But we want to use HTMX so don't break it!

So all we need to do to add CSS styles in our HTMX app is to link the stylesheet in our website's head tag - just like we would with an MPA!

<link rel="stylesheet" href="/css/app.css">

CSS in HTMX in Production

This may seem overly-simplified and like it won't work in production / be flexible enough for real-world apps. But that's not true - this is how we've built and used styles in webapps for decades and it works just as well today (and possibly better now that we have HTMX's dynamic rerender capabilities).

One common gripe I've seen from people new to HTMX is that they can't use all the nice UI technologies and styles they're used to. But if it processes down into HTML, CSS, and/or JS (essentially everything cause that's what browsers understand) then it can be used by a web page and thus by HTMX.

As an example - I used this styling approach to bundle Tailwind and DaisyUI in my recent project TravelMap:

  • At build - Compile Tailwind / DaisyUI CSS styles in to a single CSS file and place it in a static folder my site serves
  • At runtime - My page includes this style on the header and applies it to all pages, thus getting styles for all pages including those using HTMX

More details: Building ASP.NET apps with Tailwind CSS

Here's a snapshot of it's head tag:

<head>
    <meta charset="UTF-8">
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
    <link rel="stylesheet" href="/css/tailwind.css"><link rel="stylesheet" href="/css/app.css">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="0">
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin="0"></script>
    <script defer="" src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.5/dist/cdn.min.js">
    <title>TravelMap</title>
</head>

This gives me all the power of HTMX and CSS styles with basically none of the complexities of dynamic bundling.

Next

I really like HTMX for building modern web apps fast and cheap. I do most of this stuff in my spare time as a hobby so the simpler it is to build, the better it is for my projects and my own sanity.

That said - it is a very different paradigm from the clientside SPAs that have had majority webdev mindshare for the past decade. So we're venturing back into old but uncommon territory once again so there's a lot of things we'll need to rethink and relearn. Hopefully little guides like this help clarify edge cases and help you get unstuck and back to building.

Q: What questions / problems have you faced building apps with HTMX?

If you liked this post you might also like:

Want more like this?

The best / easiest way to support my work is by subscribing for future updates and sharing with your network.