Let's be honest: Half the APIs you write are for a specific purpose in a specific component on a specific page. Re-usable in theory but you've never tried.
One answer to this reality is tRPC – writing type-safe single-use APIs based on function calling. It's okay. Lots of boilerplate.
Another is to use React Server Components or approaches like TanStack Start. You write components that render on the server, grab data from regular functions, and your framework handles the rest. Mostly by hiding the RPC/API layer and dynamically swapping components into your UI tree.
But what if you don't have a JavaScript server?
HTMX
HTMX, based on good old HTTP, gives you all the benefits of server components without any of that pesky "Let's rewrite our entire server" nonsense. Perfect if you're not yet using client-side rendering (React or otherwise), don't want to, or have parts of your codebase where it isn't necessary.
We're using HTMX for our admin UIs. Lots of existing code, more and more spaghetti for interactivity, mostly forms.
Before HTMX

The typical admin page in a startup is resource-based. A God Page with everything you could ever need about a resource. Bounded contexts and workflows be damned.
The typical UX looks like this:
- Navigate to admin page
- Scroll to the right form
- Fill out fields
- Hit submit
- Get success or error
- See updated values
Maybe a sprinkle of jQuery to show/hide fields, enable a fancy date widget, or add a typeahead field. You hit submit and get a full page reload with a mix of alerts and flash messages for errors.
You don't need React for that. It's clunky but it works.
After HTMX
The biggest problem with God Page is that they are slow. And hard to maintain. And a small bug can wreck the whole resource. Someone tried to change a user name and oops the payment info is all gone.
Not to mention incompatible workflows when different kinds of users try to use the same page for different things. You'll have fun supporting that.
You don’t really have a complex app until you have 1 shared concept that 3 types of users look at differently.
— Swizec Teller (@Swizec) April 28, 2025
Same conceptual thing, many perspectives. All correct. All incompatible. All need to work together.
HTMX helps you split this into independent components.

Each section gets its own URL, becomes self-contained, isolated from the rest of the page, and you let HTMX handle the orchestration. For users who need everything, you show all the widgets. For users who need to focus, you can route them to specific pages. Your component stays the same.
HTMX, a pseudo-code example
HTMX is the missing piece in my Pattern for Composable UI. My pattern was based on function calls which felt clunky in the Jinja world. HTMX lets you hide that detail.
Small tiny views
Say you have a page that lists items from a database and a form to add more. You'll want 3 views: a list, a page, and a form.
@views.route('/list')
def list():
items = # fetch from DB
return render_html('items.html', items=items)
@views.route('/form', methods=['POST'])
def form():
if form.is_valid_submit():
# add item
response.headers["HX-Trigger"] = "itemsUpdated"
return "Item added"
else:
return "Oh no a problem"
@views.route('/page')
def page():
return render_html('page.html')
The list view returns a list of items, the form adds items and returns success or error as HTML, and the page returns HTML that ties it all together.
Basic HTML
That HTML is where it gets fun:
<div
class="items"
hx-get="/items"
hx-trigger="load, itemsUpdated from:body"
></div>
<style>
.htmx-request .loading {
display: block;
}
.htmx-request button {
display: none;
}
</style>
<form hx-post="/form" hx-indicator="this" hx-target="find .message-display">
<div class="message-display"></div>
<!-- form stuff -->
<button type="submit" />
<span class="loading">Loading ...</span>
</form>
See those hx-
attributes? They tell HTMX how to do all the dynamic stuff so you don't have to think about it.
The .items
div fetches its contents from /items
and drops them inside on page load. It reloads itself when you trigger an itemsUpdated
DOM event.
The form posts to /form
, puts itself in a loading state during the submission, then reports results into .message-display
. If you add an HX-Trigger
header to the response, it fires a DOM event that reloads any listening components.
Looks weird at first but I kinda like it.
Why HTMX?
Like I said: it's great for parts of your codebase that need light interactivity, render on the server, and deal mostly with CRUD. You could use this for super interactive UI but there's better tools for that.
Mostly HTMX is a nice way to get the benefits of composable UI development without jumping head first into a huge rewrite.
Cheers,
~Swizec
Continue reading about HTMX – Server Components without React
Semantically similar articles hand-picked by GPT-4
- Why websites and webapps are different
- A pattern for composable UI in Flask
- A few thoughts on tRPC
- The anatomy of a React Island
- Is hot dog taco?
Learned something new?
Read more Software Engineering Lessons from Production
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below 👇
Software Engineering Lessons from Production
Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️