Back to Blog
2 min readTutorial

Building Dynamic UIs with HTMX and Django

Learn how to integrate HTMX with Django to build modern, interactive web applications with minimal JavaScript

Building Dynamic UIs with HTMX and Django

HTMX and Django are a perfect match. Django handles the backend, HTMX adds interactivity. No complex JavaScript frameworks needed.

Setup

pip install django django-htmx
# settings.py
INSTALLED_APPS = [
    'django_htmx',
    # ...
]

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

Basic Example

# views.py
from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return render(request, 'index.html')

def load_users(request):
    users = User.objects.all()
    return render(request, 'partials/users.html', {'users': users})
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head>
<body>
    <button hx-get="{% url 'load_users' %}"
            hx-target="#user-list">
        Load Users
    </button>
    <div id="user-list"></div>
</body>
</html>

<!-- templates/partials/users.html -->
<ul>
{% for user in users %}
    <li>{{ user.username }}</li>
{% endfor %}
</ul>

CRUD Operations

# models.py
from django.db import models

class Task(models.Model):
    title = models.CharField(max_length=200)
    completed = models.BooleanField(default=False)

# views.py
def task_list(request):
    tasks = Task.objects.all()
    return render(request, 'tasks.html', {'tasks': tasks})

def task_create(request):
    if request.method == 'POST':
        title = request.POST.get('title')
        task = Task.objects.create(title=title)
        return render(request, 'partials/task_item.html', {'task': task})

def task_update(request, pk):
    task = Task.objects.get(pk=pk)
    task.completed = not task.completed
    task.save()
    return render(request, 'partials/task_item.html', {'task': task})

def task_delete(request, pk):
    Task.objects.filter(pk=pk).delete()
    return HttpResponse('')
<!-- tasks.html -->
<form hx-post="{% url 'task_create' %}"
      hx-target="#task-list"
      hx-swap="beforeend">
    {% csrf_token %}
    <input name="title" placeholder="New task" required />
    <button>Add</button>
</form>

<ul id="task-list">
{% for task in tasks %}
    {% include 'partials/task_item.html' %}
{% endfor %}
</ul>

<!-- partials/task_item.html -->
<li id="task-{{ task.id }}">
    <span style="{% if task.completed %}text-decoration: line-through{% endif %}">
        {{ task.title }}
    </span>
    <button hx-put="{% url 'task_update' task.id %}"
            hx-target="#task-{{ task.id }}"
            hx-swap="outerHTML">
        Toggle
    </button>
    <button hx-delete="{% url 'task_delete' task.id %}"
            hx-target="#task-{{ task.id }}"
            hx-swap="outerHTML">
        Delete
    </button>
</li>

Django + HTMX Best Practices

  1. Check HTMX requests
def my_view(request):
    if request.htmx:
        # Return partial template
        return render(request, 'partials/content.html')
    # Return full page
    return render(request, 'page.html')
  1. Use HTMX headers
from django.http import HttpResponse

def my_view(request):
    response = HttpResponse()
    response['HX-Trigger'] = 'itemUpdated'
    return response
  1. Class-based views
from django.views.generic import View

class TaskView(View):
    def get(self, request):
        tasks = Task.objects.all()
        return render(request, 'tasks.html', {'tasks': tasks})

    def post(self, request):
        title = request.POST.get('title')
        task = Task.objects.create(title=title)
        return render(request, 'partials/task_item.html', {'task': task})

Conclusion

HTMX + Django = powerful, simple web apps. Server-side rendering with modern interactivity. Try it on your next project!

👨‍💻

Jordan Patel

Web Developer & Technology Enthusiast