Litestar HTMX Plugin
litestar-htmx is an official plugin for the Litestar web framework, integrating HTMX into Litestar applications. It provides the `HTMXPlugin` for global configuration, a special `HTMXRequest` class for accessing HTMX client details, `HTMXTemplate` for rendering HTML fragments, and a suite of HTMX-specific response primitives. It streamlines server-rendered partial updates, progressive enhancement, polling, history-aware interactions, and HTMX-driven redirects. The current version is 0.5.0, actively maintained with regular updates.
Warnings
- breaking The 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`.
- gotcha When 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.
- gotcha Avoid 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.
- gotcha Ensure 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.
- breaking Prior 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`.
Install
-
pip install litestar-htmx
Imports
- HTMXPlugin
from litestar.plugins.htmx import HTMXPlugin
- HTMXRequest
from litestar.plugins.htmx import HTMXRequest
- HTMXTemplate
from litestar.plugins.htmx import HTMXTemplate
- ClientRedirect
from litestar.plugins.htmx import ClientRedirect
- PushUrl
from litestar.plugins.htmx import PushUrl
Quickstart
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