Install & Compatibility
Where this runs
tested against v1.29.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.754s · 67.2MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 3.5s · import 0.696s · 68MB
66MB installed
● package 66MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
HtmxMiddleware
✓ from django_htmx.middleware import HtmxMiddleware
Required to enable `request.htmx` and other htmx-specific request processing.
request.htmx
✓ if request.htmx: ...
Attribute added to the `HttpRequest` object by `HtmxMiddleware`, used to detect htmx requests and access htmx-specific headers (e.g., `request.htmx.boosted`, `request.htmx.current_url_abs_path`).
django_htmx_script
✓ {% load django_htmx %} {% django_htmx_script %}
Django template tag to include the `django-htmx` extension JavaScript, which provides debug error handling and other features.
HttpResponseClientRedirect
✓ from django_htmx.http import HttpResponseClientRedirect
Custom Django `HttpResponse` class for triggering client-side redirects via the `HX-Redirect` header.
HttpResponseStopPolling
✓ from django_htmx.http import HttpResponseStopPolling
Custom Django `HttpResponse` class for stopping htmx polling requests via the HTTP status code 286.
This quickstart demonstrates how to set up `django-htmx` and create a basic view that responds differently to HTMX requests. It includes necessary `settings.py` configurations, an example view using `request.htmx`, and a corresponding HTML template snippet. Remember to include the `htmx.org` JavaScript library (e.g., via CDN) and configure CSRF token handling for POST requests.
import os
from django.shortcuts import render
from django.http import HttpResponse
# settings.py additions
# INSTALLED_APPS = [
# ...,
# 'django_htmx',
# ]
# MIDDLEWARE = [
# ...,
# 'django_htmx.middleware.HtmxMiddleware',
# ]
def my_view(request):
if request.htmx:
# This branch handles HTMX requests
return HttpResponse("<div>Updated content from HTMX!</div>")
else:
# This branch handles initial page load or regular requests
context = {'initial_message': 'Click the button below to update!'}
return render(request, 'my_template.html', context)
# my_template.html (within your templates directory, inheriting a base.html)
# Assuming base.html includes the htmx.org script and {% django_htmx_script %}
# and has <body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'>
#
# {% load django_htmx %}
# <!DOCTYPE html>
# <html lang="en">
# <head>
# <meta charset="UTF-8">
# <title>Django HTMX Demo</title>
# <script src="https://unpkg.com/htmx.org@1.9.10"></script> <!-- or self-host -->
# {% django_htmx_script %}
# </head>
# <body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'>
# <div id="content">{{ initial_message }}</div>
# <button hx-get="/my-htmx-view/" hx-target="#content" hx-swap="innerHTML">Load HTMX Content</button>
# </body>
# </html>
# urls.py addition (example)
# from django.urls import path
# from . import views
#
# urlpatterns = [
# path('my-htmx-view/', views.my_view, name='my_htmx_view'),
# ]
Debug
Known issues
gotchaCSRF Token Handling for POST requests: HTMX requests do not automatically send Django's CSRF token, which is required for POST, PUT, and DELETE requests. Failure to include it will result in 403 Forbidden errors.fixAdd `hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'` to your `<body>` tag or specific elements, or include a custom JavaScript to add the header to all htmx requests. affects: All versions (Django CSRF dependent)
gotchaHTMX.org JavaScript Library is Separate: `django-htmx` provides Python utilities and template tags to *include* its own extension script, but it does NOT bundle or serve the core `htmx.org` JavaScript library itself. You must explicitly include `htmx.org` in your templates.fixInclude `htmx.org` via a CDN (`<script src="https://unpkg.com/htmx.org@latest"></script>`) or by downloading and serving it as a static file in your Django project.
affects: All versions since 1.0.0
gotcha`HtmxMiddleware` is practically essential: While technically optional, most of the core features like `request.htmx` (used to detect htmx requests and access htmx-specific headers) rely on `HtmxMiddleware` being added to your `MIDDLEWARE` setting.fixAlways include `'django_htmx.middleware.HtmxMiddleware'` in your `MIDDLEWARE` list in `settings.py` for full functionality.
affects: All versions
gotchaVary Headers for HTTP Caching: If your Django views render different HTML content for HTMX requests versus standard browser requests (using `if request.htmx:`), you must add `HX-Request` to the `Vary` header for proper HTTP caching behavior.fixUse `@vary_on_headers('HX-Request')` decorator or manually set `response['Vary'] = 'HX-Request'` in your views that differentiate content based on `request.htmx`. affects: All versions
breakingRemoval of old template tags and mixins in 1.0.0: `{% htmx_script %}` (for htmx.org), `HTMXViewMixin`, `{% htmx_include %}`, and `{% htmx_attrs %}` were removed in `django-htmx` version 1.0.0. This was a significant breaking change for users upgrading from pre-1.0.0 versions.fixUpdate your templates to directly include `htmx.org` (as mentioned above) and use the current `{% django_htmx_script %}`. Refactor views to use `request.htmx` directly instead of `HTMXViewMixin`. affects: < 1.0.0 to >= 1.0.0
breakingPotential breaking changes in `htmx.org` 4.0+: While `django-htmx` is separate, the underlying `htmx.org` library is releasing major versions (e.g., 4.0), which may introduce breaking changes to its core API, extension system, or event handling, potentially impacting custom JavaScript or advanced integrations.fixConsult the `htmx.org` migration guide (e.g., for `htmx.org` 2.0 to 4.0) and thoroughly test your application when upgrading the client-side `htmx.org` library to major versions. Pay attention to changes in the extension API.
affects: Dependent on htmx.org >= 4.0
Errors
Common errors & fixes
AttributeError: 'WSGIRequest' object has no attribute 'htmx'
This error occurs when `django-htmx`'s middleware, `HtmxMiddleware`, is not correctly added to your Django project's `MIDDLEWARE` setting, preventing the `request.htmx` attribute from being attached to the request object.
fixEnsure 'django_htmx.middleware.HtmxMiddleware' is included in your `settings.py` file, preferably after Django's `SessionMiddleware` and `CommonMiddleware`:
```python
# settings.py
INSTALLED_APPS = [
# ...
"django_htmx",
]
MIDDLEWARE = [
# ...
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django_htmx.middleware.HtmxMiddleware",
# ...
]
``` Forbidden (CSRF token missing or incorrect)
This 403 Forbidden error typically happens when making POST requests with htmx without correctly including Django's CSRF token in the request headers, as Django's `CsrfViewMiddleware` rejects requests lacking a valid token.
fixAdd the `hx-headers` attribute to your `<body>` tag (or a parent element) in your base template to automatically include the CSRF token with all htmx requests:
```html
<!-- base.html -->
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<!-- Your content -->
</body>
``` django.urls.exceptions.NoReverseMatch
This error occurs when Django's `reverse()` function or the `{% url %}` template tag cannot find a matching URL pattern for the given name and arguments, often due to a typo in the URL name, missing arguments, or an unincluded URL configuration.
fixVerify that the URL name used in `{% url 'your_url_name' %}` (or `reverse('your_url_name')`) exactly matches a `name` defined in your `urls.py`. If the URL pattern expects arguments, ensure they are correctly passed:
```python
# urls.py
path('items/<int:item_id>/', views.item_detail, name='item_detail'),
# template.html
<a href="{% url 'item_detail' item.id %}">View Item</a>
``` KeyError at /
A `KeyError` typically occurs in Django views when attempting to access a dictionary key that does not exist in the context, request data, or a dictionary derived from a JSON response, often happening during partial updates or when expected data is missing from an HTMX-triggered request.
fixBefore accessing dictionary keys, always check for their existence using `dict.get()` with a default value, or by using `if key in dict:` to prevent the error. For example, when parsing request data:
```python
# views.py
def my_view(request):
if request.htmx and request.method == 'POST':
data = request.POST
item_name = data.get('item_name', 'Default Name') # Use .get() method
# ... or check existence
if 'item_id' in data:
item_id = data['item_id']
else:
item_id = None
# ...
``` Upgrade
Version history
1.29.0latest on PyPI · released Aug 5, 2026
Audit
Dependencies
DjangorequiredCore framework dependency. Supports Django 4.2 to 6.0.
htmx.org (client-side JS library)requiredThe JavaScript library that django-htmx extends. Must be included in templates via CDN or static files.