Litestar htmx
Opinionated first-party agent skills, plugins, subagents, slash commands, and MCP servers for the Litestar framework ecosystem — publishable to Claude Code, Gemini CLI, Codex CLI, Cursor, OpenCode, and VS Code/Copilot from a single repo.
npx -y skills add litestar-org/litestar-skills --skill litestar-htmxAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 13 stars13 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Auto-activate for litestar_htmx, HTMXPlugin, HTMXConfig, HTMXRequest, HTMXTemplate, HXLocation, ReplaceUrl, TriggerEvent, HX-* headers, or Litestar partial HTML. Not for generic browser-side HTMX or Litestar Vite JSON templating — those are client concerns.
SKILL.md
12.2 KB, as published. Nobody here has run it
litestar-htmx
litestar-htmx is the standalone Litestar integration for HTMX. Version 0.5.0
ships the litestar_htmx import package with request helpers, an optional
application plugin, template responses, and typed HTMX response-header helpers.
Code Style Rules
- Import the integration from
litestar_htmx, neverlitestar.plugins.htmx; Litestar no longer owns this package's import surface. - Use
HTMXRequestwhen handlers inspect HTMX request headers. - Return template fragments from HTMX endpoints; keep full-page routes and fragment routes distinct.
- Use the response classes for
HX-*headers; do not assemble those headers by hand. - Keep browser-side HTMX extensions separate from this server package.
Quick Reference
Configure the plugin
from litestar import Litestar
from litestar_htmx import HTMXPlugin
app = Litestar(
route_handlers=[...],
plugins=[HTMXPlugin()],
)
HTMXPlugin() is the convenience path: it registers the package's request and
response types. Its default HTMXConfig(set_request_class_globally=True) sets
HTMXRequest only when the application does not already have a request class.
Preserve an existing custom request class by extending HTMXRequest:
from litestar_htmx import HTMXRequest
class ApplicationRequest(HTMXRequest):
"""Application request with HTMX helpers."""
If the application only needs response helpers, use
HTMXConfig(set_request_class_globally=False). To inspect request.htmx,
configure HTMXRequest (or a subclass) as the application request class. The
plugin never replaces a request class already present in AppConfig.
The plugin itself is optional. Applications can instead set
request_class=HTMXRequest directly and return the response subclasses without
registering HTMXPlugin.
Inspect request headers
request.htmx is always an HTMXDetails object. Its truth value is True only
when HX-Request is exactly "true".
from litestar import get
from litestar.response import Template
from litestar_htmx import HTMXRequest
@get("/items")
async def list_items(request: HTMXRequest) -> Template:
template_name = "partials/item-list.html" if request.htmx else "pages/items.html"
return Template(template_name=template_name, context={"items": []})
Available request helpers:
| Property | Source | Result |
|---|---|---|
bool(request.htmx) | HX-Request | Whether this is an HTMX request |
request.htmx.boosted | HX-Boosted | bool |
request.htmx.current_url | HX-Current-URL | str | None |
request.htmx.current_url_abs_path | HX-Current-URL | Same-origin path, query, and fragment, or None |
request.htmx.history_restore_request | HX-History-Restore-Request | bool |
request.htmx.prompt | HX-Prompt | str | None |
request.htmx.target | HX-Target | str | None |
request.htmx.trigger | HX-Trigger | str | None |
request.htmx.trigger_name | HX-Trigger-Name | str | None |
request.htmx.triggering_event | Triggering-Event | Decoded JSON value, or None |
triggering_event is supplied by HTMX's event-header extension. Malformed
JSON resolves to None. Headers accompanied by
<Header>-URI-AutoEncoded: true are URL-decoded before use.
Return template fragments with HTMX headers
HTMXTemplate extends Litestar's Template. Annotate handlers with
Template, then pass normal Template arguments plus HTMX-specific options:
from litestar import get
from litestar.response import Template
from litestar_htmx import HTMXTemplate
@get("/items/fragment")
async def item_list() -> Template:
return HTMXTemplate(
template_name="partials/item-list.html",
context={"items": []},
push_url=False,
re_swap="outerHTML",
re_target="#item-list",
trigger_event="itemsLoaded",
params={"count": 0},
after="receive",
)
trigger_event, params, and after form one event declaration. When
triggering an event, set after to "receive", "settle", or "swap".
Response helper signatures
All helpers are exported from litestar_htmx and
litestar_htmx.response.
| Helper | Constructor | Behavior |
|---|---|---|
HXStopPolling | HXStopPolling() | Returns status 286 |
ClientRedirect | ClientRedirect(redirect_to) | Sets HX-Redirect; no Location header |
ClientRefresh | ClientRefresh() | Sets HX-Refresh: true |
PushUrl | PushUrl(content, push_url, **response_kwargs) | Sets HX-Push-Url |
ReplaceUrl | ReplaceUrl(content, replace_url, **response_kwargs) | Sets HX-Replace-Url |
Reswap | Reswap(content, method, **response_kwargs) | Sets HX-Reswap |
Retarget | Retarget(content, target, **response_kwargs) | Sets HX-Retarget |
TriggerEvent | TriggerEvent(content, name, after, params=None, **response_kwargs) | Sets the selected HX-Trigger* header |
HXLocation | HXLocation(redirect_to, source=None, event=None, target=None, select=None, swap=None, hx_headers=None, values=None, **response_kwargs) | Sets JSON in HX-Location |
push_url=False and replace_url=False emit "false" to prevent the
corresponding history update.
Soft navigation with HXLocation
Use HXLocation for an HTMX navigation request without a full-page reload.
select chooses a fragment from the fetched response before it is swapped:
from litestar import post
from litestar_htmx import HXLocation
@post("/items")
async def create_item() -> HXLocation:
return HXLocation(
redirect_to="/items",
source="#create-item",
event="submit",
target="#content",
select="#item-list",
swap="innerHTML",
hx_headers={"X-View": "compact"},
values={"created": "true"},
)
The response uses status 200, carries HX-Location, and removes the ordinary
Location header.
Trigger an event while returning content
TriggerEvent requires the response content, event name, and after
phase:
from litestar import post
from litestar_htmx import TriggerEvent
@post("/items")
async def create_item() -> TriggerEvent[str]:
return TriggerEvent(
content="<li>Saved</li>",
name="itemCreated",
after="swap",
params={"id": 42},
media_type="text/html",
)
Prefer HTMXTemplate when the content is HTML assembled from application data.
Litestar Vite is a separate client layer
The standalone package owns Python request parsing and response headers:
from litestar_htmx import HTMXPlugin, HTMXRequest, HTMXTemplate
Litestar Vite's hx-ext="litestar" JSON templating and CSRF integration come
from the separate litestar-vite-plugin/helpers JavaScript export. They are not
installed, registered, or enabled by HTMXPlugin(). Use them only when the
project already uses Litestar Vite and needs client-side JSON swaps. See
Litestar Vite Integration.
Workflow
- Check the project's installed
litestar-htmxversion and existing request class. - Register
HTMXPlugin()or setrequest_class=HTMXRequestdirectly. ExtendHTMXRequestwhen the application needs custom request behavior. - Separate full-page endpoints from fragment endpoints. Branch on
request.htmxonly when one URL intentionally supports both. - Render fragments with
TemplateorHTMXTemplate. - Select the narrow response helper matching the required HTMX header.
- Configure CSRF protection for every state-changing HTMX request.
- Test the response body, status, and exact
HX-*header. - Add Litestar Vite's client extension only for bundled assets, CSRF header injection, or JSON templating.
Guardrails
- Use
litestar_htmx, neverlitestar.plugins.htmx. The 0.5.0 package is a standalone distribution with its own public import root. - Pass every required response-helper argument.
TriggerEventrequirescontent,name, andafter;PushUrl,ReplaceUrl,Reswap, andRetargetalso require content. - Use
select=onHXLocationto choose returned content. Do not confuse it withtarget=, which chooses the receiving element. - Do not assume
HTMXPluginoverrides an existing request class. It preserves a non-nullAppConfig.request_class. - Do not treat
request.htmxas an optional object. Test its truth value to identify HTMX requests. - Do not send a normal redirect for
HXLocationorClientRedirect. These helpers return200with HTMX response headers. - Do not attribute
hx-ext="litestar"tolitestar-htmx. That browser extension ships with Litestar Vite's npm package. - Do not return unsanitized, concatenated HTML. Render templates so escaping and template caching remain intact.
Validation Checkpoint
- Imports use
litestar_htmx, notlitestar.plugins.htmx -
HTMXPlugin()orrequest_class=HTMXRequestwires request helpers - A custom global request class extends
HTMXRequest - Full pages and HTMX fragments have explicit boundaries
-
TriggerEventincludescontent,name, and a validaftervalue -
ReplaceUrlusesreplace_url=, notpush_url= -
HXLocation.selectandHXLocation.targetserve distinct purposes - State-changing HTMX requests include the application's CSRF token
- Tests assert the exact status, body, and
HX-*response header - Litestar Vite client-extension guidance is identified as a separate layer
Example
Return a fragment, retarget the swap, prevent a history update, and verify the HTMX response:
from litestar import Controller, get
from litestar.response import Template
from litestar_htmx import HTMXRequest, HTMXTemplate
class ItemController(Controller):
path = "/items"
@get("/")
async def index(self, request: HTMXRequest) -> Template:
items = [{"id": 1, "name": "Widget"}]
if request.htmx:
return HTMXTemplate(
template_name="partials/item-list.html",
context={"items": items},
re_target="#item-list",
re_swap="outerHTML",
push_url=False,
)
return Template(template_name="pages/items.html", context={"items": items})
async def test_htmx_item_list(client) -> None:
response = await client.get(
"/items/",
headers={"HX-Request": "true", "HX-Target": "item-list"},
)
assert response.status_code == 200
assert response.headers["HX-Retarget"] == "#item-list"
assert response.headers["HX-Reswap"] == "outerHTML"
assert response.headers["HX-Push-Url"] == "false"
assert "<html" not in response.text
</example>
References Index
- Litestar Vite Integration — Keep the standalone Python package distinct from Litestar Vite's browser extension.
- Litestar — Application setup, templates, and lifecycle fundamentals.
- Litestar Vite — Asset bundling, template mode, HMR, and the client helper package.
- Litestar Testing — Async clients and application fixtures.
Official References
- https://pypi.org/project/litestar-htmx/0.5.0/
- https://github.com/litestar-org/litestar-htmx/tree/v0.5.0/litestar_htmx
- https://github.com/litestar-org/litestar-htmx/blob/v0.5.0/litestar_htmx/request.py
- https://github.com/litestar-org/litestar-htmx/blob/v0.5.0/litestar_htmx/response.py
- https://github.com/litestar-org/litestar-htmx/tree/v0.5.0/tests
- https://htmx.org/reference/