Install & Compatibility
Where this runs
tested against v0.5.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.000s · 17.9MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 1.5s · import 0.000s · 18MB
16MB installed
● package 16MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
HTMXPlugin
✓ from litestar_htmx import HTMXPlugin
✗ from litestar_htmx import HTMXPlugin
HTMXRequest
✓ from litestar_htmx import HTMXRequest
This quickstart demonstrates setting up a Litestar application with `litestar-htmx`. It configures the `HTMXPlugin` globally and uses `HTMXRequest` to detect HTMX requests. The example includes two routes: one for a full page load and another for an HTMX-driven partial update using `HTMXTemplate`. It also shows a POST request for an HTMX interaction.
import os
from pathlib import Path
from litestar import Litestar, get
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.template.config import TemplateConfig
from litestar.plugins.htmx import HTMXPlugin, HTMXRequest, HTMXTemplate
from litestar.response import Template
# Create a dummy template directory and file for the example
TEMPLATE_DIR = Path("./templates")
TEMPLATE_DIR.mkdir(exist_ok=True)
(TEMPLATE_DIR / "full_page.html").write_text(
"""
<!DOCTYPE html>
<html>
<head><title>Full Page</title></head>
<body>
<h1>Full Page Content</h1>
<div id="content" hx-get="/partial" hx-trigger="load">Loading partial...</div>
</body>
</html>
"""
)
(TEMPLATE_DIR / "partial.html").write_text(
"""
<p>This is a partial loaded by HTMX!</p>
<button hx-post="/clicked" hx-target="#message">Click Me</button>
<div id="message"></div>
"""
)
@get(path="/")
async def get_full_page(request: HTMXRequest) -> Template:
if request.htmx:
# This branch should ideally not be hit for the root page on initial load
# but demonstrates checking for HTMX request.
return HTMXTemplate(template_name="partial.html", context={"is_htmx": True})
return Template(template_name="full_page.html")
@get(path="/partial")
async def get_partial(request: HTMXRequest) -> HTMXTemplate:
# This handler expects an HTMX request
return HTMXTemplate(template_name="partial.html", context={"from_partial": True})
@post(path="/clicked")
async def post_clicked() -> HTMXTemplate:
return HTMXTemplate(template_name="<p>Button was clicked!</p>") # Inline template
app = Litestar(
route_handlers=[get_full_page, get_partial, post_clicked],
plugins=[HTMXPlugin()],
template_config=TemplateConfig(
directory=TEMPLATE_DIR,
engine=JinjaTemplateEngine,
),
debug=True
)
# To run this:
# 1. Save as e.g., `app.py`
# 2. `pip install litestar litestar-htmx jinja2 uvicorn`
# 3. `uvicorn app:app --reload`
# 4. Navigate to http://127.0.0.1:8000
Debug
Known issues
breakingThe HTMX integration was migrated from `litestar.contrib.htmx` to `litestar.plugins.htmx` as a standalone plugin. Users upgrading from Litestar versions where HTMX was part of `litestar.contrib` must update their imports and integrate the `HTMXPlugin`.fixUpdate imports to `from litestar.plugins.htmx import ...` and add `HTMXPlugin()` to `Litestar` application's `plugins` list.
affects: <2.x (where `litestar.contrib.htmx` was used)
gotchaWhen defining route handlers that return `HTMXTemplate`, the return type annotation should be `litestar.response.Template`, not `HTMXTemplate` directly. `HTMXTemplate` is a specialized `Template` for HTMX-specific headers.fixAnnotate handlers as `-> Template` (imported from `litestar.response`) even if the actual return type is `HTMXTemplate`.
affects: All versions of `litestar-htmx`
gotchaAvoid manually parsing raw `HX-*` headers from the request. The `HTMXRequest` class provides a convenient `request.htmx` object (an `HTMXDetails` instance) with strongly typed properties for all HTMX-specific headers.fixAccess HTMX request details via `request.htmx.header_name` (e.g., `request.htmx.current_url`) instead of `request.headers.get('HX-Current-URL')`. affects: All versions of `litestar-htmx`
gotchaEnsure that HTMX responses return only the HTML fragment expected by the client's `hx-target`. Accidentally returning a full page layout into a fragment target can lead to UI breakage.fixDesign handlers and templates so that HTMX requests always return small, swap-oriented fragments. Use conditional logic (`if request.htmx:`) to render different content for HTMX vs. non-HTMX requests if a route serves both.
affects: All versions of `litestar-htmx`
breakingPrior to version 0.5.0, `HTMXRequest` was not generic in `UserT`, `AuthT`, and `StateT`. This meant that type-safe access to `request.user`, `request.auth`, or `request.state` (if configured with custom types) was not properly propagated through `HTMXRequest`.fixUpgrade to `litestar-htmx` version 0.5.0 or newer. This issue was resolved in PR #14, making `HTMXRequest` generic like `litestar.Request`.
affects: <0.5.0
Upgrade
Version history
0.5.0latest on PyPI · released Jun 11, 2025
Audit
Dependencies
litestarrequiredCore dependency for the plugin to function within a Litestar application.