Building a Dynamic Django BackOffice with HTMX

How Django and HTMX can work together to create interactive BackOffice applications without the need for complex JavaScript code.

Building a Dynamic Django BackOffice with HTMX

Why Use Django with HTMX?

HTMX is a powerful JavaScript library that enhances Django by allowing you to update parts of a web page asynchronously without writing complex JavaScript. Unlike modern frameworks like React or Vue, which require heavy client-side logic and state management, HTMX enables a more traditional server-rendered approach while still providing a dynamic user experience.

Benefits of HTMX Over JavaScript Frameworks

  • Less JavaScript: No need for complex frontend state management.
  • Better Performance: Pages load faster as only small portions are updated via AJAX.
  • Simpler Debugging: Everything is still server-rendered, making debugging easier.
  • SEO Friendly: Content remains accessible to search engines without extra work.

HTMX vs. Traditional jQuery/Ajax and JavaScript Frameworks

HTMX provides a middle ground between traditional jQuery/Ajax solutions and modern JavaScript frameworks like Next.js or Vue. Here's how they compare:

  • Compared to jQuery/Ajax:
    • HTMX simplifies AJAX requests with declarative attributes instead of writing JavaScript event handlers.
    • jQuery requires manually handling responses and updating the DOM, whereas HTMX seamlessly integrates server responses into the page.
    • HTMX includes built-in support for triggers, swapping, and progressive enhancement, reducing boilerplate code.
  • Compared to Next.js/React/Vue:
    • React and Vue shift rendering to the client-side, often requiring a backend API, while HTMX keeps Django as the primary rendering engine.
    • HTMX eliminates the need for state management libraries, unlike modern frontend frameworks that require complex state handling (e.g., Redux, Vuex).
    • Server-side rendering in React (e.g., Next.js) adds complexity with hydration, whereas HTMX dynamically loads content with minimal overhead.
    • HTMX has a smaller footprint, leading to faster initial page loads and better performance on low-powered devices.

HTMX is a great choice for projects that need interactivity without a full frontend framework. However, if you need advanced client-side state management or a single-page application (SPA), React or Vue might be a better fit.


Building a Django BackOffice with HTMX

Let's assume we're building a BackOffice for managing products in an inventory system. We will use HTMX for the following functionalities:

  1. Modal Dialogs for Editing Products
  2. Infinite Scrolling for Product Listings
  3. Filtering, Pagination, and Sorting for Product Tables

Setting Up Django with HTMX

First, install Django and HTMX:

pip install django django-htmx

Add django_htmx to INSTALLED_APPS in settings.py and configure middleware:

MIDDLEWARE = [
    ...,
    'django_htmx.middleware.HtmxMiddleware',
]

Include the HTMX library in your template:

<script src="https://unpkg.com/[email protected]"></script>

1. Modal Dialogs for Editing Products

A common BackOffice feature is editing items in a modal without a full page reload.

Template: Button to Open the Modal

<button hx-get="/products/1/edit/" hx-target="#modal" hx-trigger="click">
  Edit
</button>

Django View: Rendering the Modal

from django.shortcuts import render, get_object_or_404
from .models import Product

def edit_product(request, product_id):
    product = get_object_or_404(Product, id=product_id)

    return render(
        request,
        "products/edit_modal.html", {"product": product}
    )

HTMX Target Modal Container

<div id="modal"></div>

This approach loads and injects the modal into the page asynchronously.


2. Infinite Scrolling for Product Listings

Instead of paginating with page reloads, we can use infinite scrolling.

HTML Template: Product List

<div
  id="product-list"
  hx-get="/products/"
  hx-trigger="revealed"
  hx-swap="beforeend"
></div>

The hx-trigger="revealed" ensures new products load when the last element becomes visible.

Django View: Infinite Scroll Response

from django.core.paginator import Paginator

def product_list(request):
    page = int(request.GET.get("page", 1))
    products = Product.objects.all()
    paginator = Paginator(products, 10)

    return render(
        request,
        "products/partials/product_lista", {"products": paginator.get_page(page)}
    )

3. Table Filtering, Pagination, and Sorting

Users often need to filter, paginate, and sort tables dynamically.

HTML Table

<input
  type="text"
  hx-get="/products/"
  hx-target="#product-table"
  hx-trigger="keyup delay:500ms"
/>
<table>
  <thead>
    <tr>
      <th>
        <a hx-get="/products/?sort=name" hx-target="#product-table">Name</a>
      </th>
      <th>
        <a hx-get="/products/?sort=price" hx-target="#product-table">Price</a>
      </th>
    </tr>
  </thead>
  <tbody id="product-table">
    {% include "products/partials/product_table.html" %}
  </tbody>
</table>

Django View: Handling Sorting and Filtering

def product_table(request):
    search_query = request.GET.get("search", "")
    sort_by = request.GET.get("sort", "name")
    products = Product.objects.filter(
        name__icontains=search_query
    ).order_by(sort_by)

    return render(
        request,
        "products/partials/product_table.html", {"products": products}
    )

Conclusion

HTMX is a great alternative to modern JavaScript frameworks for building dynamic Django applications. It keeps things simple, improves performance, and reduces frontend complexity. Whether you're building modals, infinite scrolling, or interactive tables, HTMX makes Django-powered BackOffice applications much smoother to use.

More posts in Python