top of page
  • Writer's pictureCraig Anderson

HTMX & Django—bringing the new school to the old school

HTMX is a new JavaScript framework.

If you're anything like me, that's normally enough to make you roll your eyes, close this browser tab and get on with your day. But wait! HTMX is different! It's a framework which embraces the old school. It plays well with any server-side framework, and embraces the RESTful architecture of the web.

Read on for a bit of background, or jump ahead to see how it can be integrated into your existing Django app.

A bit of context

Since the introduction of jQuery in 2006, JavaScript frameworks have come a long way.

Back then, well-designed web applications were tied to how HTTP works—you could get stuff from URLs, you could send stuff to URLs, and that was about it. This application architecture is known as REST (Representational state transfer), and it's a fundamental part of the web.

A downside of these apps was that they weren't dynamic. If you were looking at a list of chat messages, for example, you'd need to refresh the page to see any new messages.

These apps weren't dynamic, but they were simple enough for a small team to maintain.

Evolution brings dynamism and complexity

As the web evolved, we figured out ways to make our web apps more dynamic. JavaScript frameworks like Angular and React let us build complex interfaces which respond quickly to changes, and technologies like WebSockets let us establish a two-way communication pipe between the browser and server.

Today, web applications like Twitter and Facebook let us have real-time conversations with our friends without ever having to refresh the page.

These frameworks rewrote the rules on how web applications could be built, and we've all benefited greatly from it.

Unfortunately, web applications have become very complicated as a result. They require teams of engineers with specialised skills. And although they run on the web, their architectures often don't reflect REST. A developer can't necessarily transfer their architecture knowledge and experience from one web application to another.

Back to the old school

Through all this evolution, people are still building web applications using REST. Server-side frameworks like Django let you set up the basic pieces of a REST application very easily and focus on building what's important in your app. But these apps are still, for the most part, static.

This is where HTMX comes in.

HTMX lets you use REST actions on parts of a web page, triggered by any user event. This lets our REST applications become dynamic! We can send chat messages to our friends as we type them, and automatically update our message inbox.

A practical example

At My Data Chameleon, we've spent months building complex data checking rules to guide users through optimising their data for the CPUC CET. But users were only seeing the results of that work after filling in tens of fields and clicking a button.

With HTMX and an afternoon of work, we were able to give users dynamic feedback as they fixed their data.

The world of energy efficiency isn't easy to understand, so I've come up with an example form to demonstrate how we've used HTMX to give users dynamic feedback on the data they've entered.

This example form lets you order a meal for one or more people. It contains:

  • a meal selection drop-down list—this includes meals which serve two or more people;

  • a numeric field where you can enter how many people will be eating; and

  • a list of checkboxes where you can select dietary requirements.

It's invalid to select the roast chicken for four if there's only one person eating. It's also invalid to select the roast chicken for a group of four containing a vegan.

Implementing validation server-side

This validation is implemented server-side in a Django form's clean method:

def clean(self):

    # …setup…

    if self.cleaned_data["vegetarian"] and meal.contains_meat:
            "meal", "Meal not suitable for vegetarians."

    # …and so on

Adding HTMX for dynamic validation

Normally, the user would need to submit the form to see if their selected options are valid or not, but using HTMX we can give the user immediate feedback.

To set this up, install HTMX on your site, then add HTMX attributes as shown below:

<form method="POST" id="order-meal-form"
    hx-post="{% url 'order-meal' %}"

These hx- attributes tell HTMX what to do. They are:

  • hx-trigger defines the event that will trigger HTMX.

  • hx-post tells HTMX where to send a POST request. The response to this request will be inserted into this page.

  • hx-select is a CSS selector defining the part of the response to use.

  • hx-swap tells HTMX how to swap content (by default HTMX would insert the response inside this form, which would result in nested forms).

This form now updates itself whenever the user enters any invalid combination, giving them dynamic feedback on the data they've entered. But when a valid combination is entered, the form resets to its initial state.

Handling valid combinations

At the moment, the view doesn't distinguish between valid orders submitted by the user and valid orders submitted via HTMX. This is resulting in the form resetting as soon as a valid combination is selected. To stop this from happening, we can check for the HTMX request header when processing a valid form and, if it's present, returning an appropriate response:

def form_valid(self, form):
    """Process a valid form."""

    # If HTMX, just render the form.
    if "HTTP_HX_REQUEST" in self.request.META:
        return self.render_to_response(

    messages.success(self.request, "Order successfully placed!")
    return super().form_valid(form)


And that is that! This form now updates itself whenever the user changes any field, giving immediate feedback on the data they've entered without having to re-implement any of our validation client-side.

Feel free to see this in action in our demo app. If you want to see this demo app's code, it's available on GitHub.

If you'd like to share any thought about this article, please reach out! You can reach Sharper Informatics Solutions on @SharperInfo, or get in touch with the author directly at @_craiga.


bottom of page