HTMX integration for ASGI applications. Works with Starlette, FastAPI, Quart -- or any other web framework supporting ASGI that exposes the ASGI scope. Inspired by django-htmx.
Table of contents
NOTE: This is alpha software. Please be sure to pin your dependencies.
pip install asgi-htmx==0.1.*
First, ensure HTMX is installed.
For example, download a copy of htmx.min.js, add it to your static files, then add the script tag to templates:
<script src="{{ url_for('static', path='/js/htmx.min.js') }}" defer></script>Now, install HtmxMiddleware onto the ASGI app:
-
Using Starlette:
from asgi_htmx import HtmxMiddleware from starlette.middleware import Middleware app = Starlette( middleware=[ ..., Middleware(HtmxMiddleware), ..., ], )
-
Using FastAPI:
from asgi_htmx import HtmxMiddleware from fastapi import FastAPI app = FastAPI() app.add_middleware(HtmxMiddleware)
You can now access scope["htmx"] (an instance of HtmxDetails) in endpoints:
# `HtmxRequest` makes code editors type-check `request.scope["htmx"]`
from asgi_htmx import HtmxRequest as Request
from .resources import templates
async def home(request: Request):
template = "home.html"
context = {"request": request}
if (htmx := request.scope["htmx"]):
template = "partials/items.html"
context["boosted"] = htmx.boosted # ...
return templates.TemplateResponse(template, context)See examples for full working example code.
An ASGI middleware that sets scope["htmx"] to an instance of HtmxDetails (scope refers to the ASGI scope).
app = HtmxMiddleware(app)A helper that provides shortcuts for accessing HTMX-specific request headers.
htmx = HtmxDetails(scope)__bool__() -> bool- ReturnTrueif the request was made using HTMX (HX-Requestis present),Falseotherwise.boosted: bool- Mirrors theHX-Boostedheader:Trueif the request is via an element with thehx-boostattribute.current_url: str | None- Mirrors theHX-Current-URLheader: The current URL of the browser, orNonefor non-HTMX requests.history_restore_request: str- Mirrors theHX-History-Restore-Requestheader:Trueif the request is for history restoration after a miss in the local history cache.prompt: str | None- MirrorsHX-Prompt: The user response tohx-promptif it was used, orNone.target: str | None- MirrorsHX-Target: Theidof the target element if it exists, orNone.trigger: str | None- MirrorsHX-Trigger: Theidof the trigger element if it exists, orNone.trigger_name: str | None- MirrorsHX-Trigger-Name: Thenameof the trigger element if it exists, orNone.triggering_event: Any | None- MirrorsTriggering-Event, which is set by the event-header extension: The deserialized JSON representation of the event that triggered the request if it exists, orNone.
For Starlette-based frameworks, use this instead of the standard starlette.requests.Request so that code editors understand that request.scope["htmx"] contains an HtmxDetails instance:
from asgi_htmx import HtmxRequest as Request
async def home(request: Request):
reveal_type(request.scope["htmx"]) # Revealed type is 'HtmxDetails'MIT