iet-http-htmx-axum
Renders server-side HTML, validates forms, and supports HTMX without requiring JavaScript.
iet-http-htmx-axumadapterHTML/HTMX transport adapterHow to use it
iet-http-htmx-axum provides HTML/HTMX transport helpers on top of Axum: HtmlPage/HtmlFragment responses, HtmxRequest for reading presentation hints, safe redirect/htmx_redirect, HtmlError for private error pages, and a bounded ValidatedForm extractor. These are additive pieces an app merges with its own Axum routes.
// crates/features/iet-http-htmx-axum/tests/forms.rs
use ;
use ValidatedForm;
async
ValidatedFormenforces the form content type, a 16 KiB body limit, andValidate; rejections are415,400,413, or422and never echo the body.- Read hints with
HtmxRequest::new(&headers).is_htmx()/.target(); turn anAppErrorinto a page withHtmlError(error)(addsno-store). redirect(&headers, "/path")selectsHX-Redirectfor HTMX requests and303 Locationotherwise, and rejects non-local paths.
How it works
ValidatedForm is an Axum FromRequest extractor that rejects untrusted input before the handler. decode_form checks the URL-encoded content type, reads the body under a byte limit, deserializes with serde_urlencoded, and then Validate::validate runs, mapping each failure to a plain, non-reflective status.
flowchart LR
A["ValidatedForm::from_request"] --> B["decode_form"]
B --> C{"form content type"}
C -->|no| D["415 Unsupported Media Type"]
C -->|yes| E["to_bytes with 16 KiB limit"]
E -->|over| F["413 Payload Too Large"]
E --> G["serde_urlencoded::from_bytes"]
G --> H["Validate::validate"]
H -->|invalid| I["422 Unprocessable Entity"]
H -->|valid| J["handler receives T"]What it implements
- Concrete mechanism; see the public API below.
What's inside
Modules
src/askama.rssrc/assets.rssrc/cache.rssrc/compiled_ui.rssrc/deferred.rssrc/form.rssrc/lib.rssrc/request.rssrc/response.rs
Public API
askama
- struct
AskamaPage— Render an Askama template as a complete HTML document. Askama's HTML autoescaping is retained; template rendering failures map to 500. - struct
AskamaFragment— Render an Askama template as an HTML fragment. Askama's HTML autoescaping is retained; template rendering failures map to 500.
assets
- const
HTMX_JS - const
ALPINE_JS - struct
BundledUiAssets pub fn with_htmx(htmx: &'static [u8], content_hash: &'static str) -> UiAssets— Mount bundled Alpine and Tailwind files at versioned routes alongside pinned HTMX bytes.pub fn router(content_hash: &'static str) -> axum::Router— Mount all bundled assets at versioned routes.pub fn bundled_router() -> axum::Router— Mount the bundled releases at their release-derived immutable version path.pub fn bundled_tags() -> String— Return script tags for [Self::bundled_router].- struct
UiAssets— Build routes that serve local HTMX, Alpine.js, and Tailwind browser-build assets. The provided bytes must be locally built or vendor-pinned files. The framework intentionally does not fetch CDN content or impose a Node build step. Supply a version token that changes whenever any asset changes before enabling immutable caching. - const
fn— Construct the asset bundle.content_hashshould identify these exact asset bytes. pub fn router(self) -> axum::Router— Return a router serving the asset paths referenced by [Self::tags].pub fn tags(&self) -> String— Return asset tags that match the mounted versioned routes.pub fn tags(content_hash: &str) -> String— Return matching deferred script tags for all bundled UI assets.
cache
pub fn no_store(mut response: Response) -> Response— Mark a response private, including browser and intermediary caches.pub fn public(mut response: Response, request: &HeaderMap, etag: &str, seconds: u32) -> Response— Cache a successful public representation with a caller-supplied quoted ETag. The validator must identify the entire representation, not just its route.
compiled_ui
pub fn script_router(scripts: &[Script]) -> Router— Register content-addressed JavaScript modules once at the composition root.- const
COMPONENT_RUNTIME_SOURCE pub fn component_router(scripts: &[Script]) -> Router
deferred
- struct
DeferredResponse— Versioned NDJSON envelopes containing HTML, never client-side domain state. Up to 16 concurrent jobs, each with a timeout and a 1 MiB HTML limit. pub fn new() -> Selfpub fn timeout(mut self, timeout: Duration) -> Selfpub fn boundary<F>(mut self, id: &str, render: F) -> AppResult<Self> where F: Future<Output = AppResult<String>> + Send + 'static,pub fn runtime_router() -> axum::Router— Small optional runtime; mount once beside the existing HTMX asset router.
form
- struct
ValidatedForm— Bounded URL-encoded form data validated before reaching a handler.
request
- struct
HtmxRequest pub fn new(headers: &'a HeaderMap) -> Selfpub fn is_htmx(self) -> boolpub fn is_boosted(self) -> boolpub fn target(self) -> Option<&'a str>pub fn current_url(self) -> Option<&'a str>pub fn vary(self, response: &mut axum::http::Response<axum::body::Body>)
response
- struct
HtmlError— Safe browser error presentation. Never reflects application messages or fields. pub fn redirect(headers: &axum::http::HeaderMap, path: &str) -> Result<Response, AppError>— Select redirect semantics from presentation hints, never authorization hints.- struct
HtmlPage - struct
HtmlFragment - struct
HtmlRedirect - struct
HtmlResponse - struct
CompiledHtml pub fn page(self) -> HtmlPage<T>pub fn fragment(self) -> HtmlFragment<T>pub fn htmx_redirect(path: &str) -> Result<Response, AppError>— Create an HTMX redirect response after validating a same-origin path.pub fn safe_local_path(path: &str) -> bool
Impl map
IntoResponse for AskamaPage<T>IntoResponse for AskamaFragment<T>Default for DeferredResponseIntoResponse for DeferredResponseFromRequest<S> for ValidatedForm<T>From<AppError> for HtmlErrorIntoResponse for HtmlErrorFrom<iet_ui::Markup> for HtmlPage<CompiledHtml>From<iet_ui::Markup> for HtmlFragment<CompiledHtml>IntoResponse for HtmlPage<CompiledHtml>IntoResponse for HtmlFragment<CompiledHtml>IntoResponse for HtmlPage<T>IntoResponse for HtmlFragment<T>IntoResponse for HtmlRedirect<'_>Validate for Search
Depends on
External crates: askama, axum, futures-util, http-body-util, include_dir, serde, serde_json, serde_urlencoded, tokio, tracing
Cargo features
default— (no dependencies)askama— dep:askamaui-assets— dep:include_dirprogressive— dep:futures-util, dep:serde_json, dep:tokiocompiled-ui— dep:iet-uicomponents— compiled-ui, iet-ui/scripts
Used by
In this repository
examples/htmx-starterexamples/skills-only-appexamples/skills-only-app-task-tracker
Source: crates/features/iet-http-htmx-axum