Expand description
§axum-htmx
axum-htmx is a small extension library providing extractors, responders, and
request guards for htmx headers within
axum.
§Table of Contents
- Getting Started
- Extractors
- Responders
- Auto Caching Management
- Request Guards
- Examples
- Feature Flags
- Contributing
- License
§Getting Started
Run cargo add axum-htmx to add the library to your project.
§Extractors
All of the htmx request headers
have a supported extractor. Extractors are infallible, meaning they will always
succeed and never return an error. In the case where a header is not present,
the extractor will return None or false dependant on the expected return
type.
| Header | Extractor | Value |
|---|---|---|
HX-Boosted | HxBoosted | bool |
HX-Current-URL | HxCurrentUrl | Option<axum::http::Uri> |
HX-History-Restore-Request | HxHistoryRestoreRequest | bool |
HX-Prompt | HxPrompt | Option<String> |
HX-Request | HxRequest | bool |
HX-Target | HxTarget | Option<String> |
HX-Trigger-Name | HxTriggerName | Option<String> |
HX-Trigger | HxTrigger | Option<String> |
§Responders
All of the htmx response headers
have a supported responder. A responder is a basic type that implements
IntoResponseParts, allowing you to simply and safely apply the HX-* headers to
any of your responses.
| Header | Responder | Value |
|---|---|---|
HX-Location | HxLocation | String |
HX-Push-Url | HxPushUrl | String |
HX-Redirect | HxRedirect | String |
HX-Refresh | HxRefresh | bool |
HX-Replace-Url | HxReplaceUrl | String |
HX-Reswap | HxReswap | axum_htmx::responders::SwapOption |
HX-Retarget | HxRetarget | String |
HX-Reselect | HxReselect | String |
HX-Trigger | HxResponseTrigger | axum_htmx::serde::HxEvent |
HX-Trigger-After-Settle | HxResponseTrigger | axum_htmx::serde::HxEvent |
HX-Trigger-After-Swap | HxResponseTrigger | axum_htmx::serde::HxEvent |
§Vary Responders
Also, there are corresponding cache-related headers, which you may want to add to
GET responses, depending on the htmx headers.
For example, if your server renders the full HTML when the HX-Request header is
missing or false, and it renders a fragment of that HTML when HX-Request: true,
you need to add Vary: HX-Request. That causes the cache to be keyed based on a
composite of the response URL and the HX-Request request header - rather than
being based just on the response URL.
Refer to caching htmx docs section for details.
| Header | Responder |
|---|---|
Vary: HX-Request | VaryHxRequest |
Vary: HX-Target | VaryHxTarget |
Vary: HX-Trigger | VaryHxTrigger |
Vary: HX-Trigger-Name | VaryHxTriggerName |
Look at the Auto Caching Management section for
automatic Vary headers management.
§Auto Caching Management
Requires feature auto-vary.
Manual use of Vary Reponders adds fragility to the code, because of the need to manually control correspondence between used extractors and the responders.
We provide a middleware to address this issue by
automatically adding Vary headers when corresponding extractors are used.
For example, on extracting HxRequest, the middleware automatically adds
Vary: hx-request header to the response.
Look at the usage example.
§Request Guards
Requires feature guards.
In addition to the extractors, there is also a route-wide layer request guard
for the HX-Request header. This will redirect any requests without the header
to “/” by default.
It should be noted that this is NOT a replacement for an auth guard. A user can
trivially set the HX-Request header themselves. This is merely a convenience
for preventing users from receiving partial responses without context. If you
need to secure an endpoint you should be using a proper auth system.
§Examples
§Example: Extractors
In this example, we’ll look for the HX-Boosted header, which is set when
applying the hx-boost attribute to an
element. In our case, we’ll use it to determine what kind of response we send.
When is this useful? When using a templating engine, like
minijinja, it is common to extend
different templates from a _base.html template. However, htmx works by sending
partial responses, so extending our _base.html would result in lots of extra
data being sent over the wire.
If we wanted to swap between pages, we would need to support both full template
responses and partial responses (as the page can be accessed directly or
through a boosted anchor), so we look for the HX-Boosted header and extend
from a _partial.html template instead.
use axum::response::IntoResponse;
use axum_htmx::HxBoosted;
async fn get_index(HxBoosted(boosted): HxBoosted) -> impl IntoResponse {
if boosted {
// Send a template extending from _partial.html
} else {
// Send a template extending from _base.html
}
}§Example: Responders
We can trigger any event being listened to by the DOM using an htmx trigger header.
use axum_htmx::HxResponseTrigger;
// When we load our page, we will trigger any event listeners for "my-event.
async fn index() -> (HxResponseTrigger, &'static str) {
// Note: As HxResponseTrigger only implements `IntoResponseParts`, we must
// return our trigger first here.
(
HxResponseTrigger::normal(["my-event", "second-event"]),
"Hello, world!",
)
}htmx also allows arbitrary data to be sent along with the event, which we can
use via the serde feature flag and the HxEvent type.
use serde_json::json;
// Note that we are using `HxResponseTrigger` from the `axum_htmx::serde` module
// instead of the root module.
use axum_htmx::{HxEvent, HxResponseTrigger};
async fn index() -> (HxResponseTrigger, &'static str) {
let event = HxEvent::new_with_data(
"my-event",
// May be any object that implements `serde::Serialize`
json!({"level": "info", "message": {
"title": "Hello, world!",
"body": "This is a test message.",
}}),
)
.unwrap();
// Note: As HxResponseTrigger only implements `IntoResponseParts`, we must
// return our trigger first here.
(HxResponseTrigger::normal([event]), "Hello, world!")
}§Example: Router Guard
use axum::Router;
use axum_htmx::HxRequestGuardLayer;
fn router_one() -> Router {
Router::new()
// Redirects to "/" if the HX-Request header is not present
.layer(HxRequestGuardLayer::default())
}
fn router_two() -> Router {
Router::new()
.layer(HxRequestGuardLayer::new("/redirect-to-this-route"))
}§Feature Flags
| Flag | Default | Description | Dependencies |
|---|---|---|---|
auto-vary | Disabled | A middleware to address htmx caching issue | futures, tokio, tower |
guards | Disabled | Adds request guard layers. | tower, futures-core, pin-project-lite |
serde | Disabled | Adds serde support for the HxEvent and LocationOptions | serde, serde_json |
§Contributing
Contributions are always welcome! If you have an idea for a feature or find a bug, let me know. PR’s are appreciated, but if it’s not a small change, please open an issue first so we’re all on the same page!
§Testing
cargo +nightly test --all-features§License
axum-htmx is dual-licensed under either
at your option.
Modules§
- auto_
vary auto-vary - A middleware to automatically add a
Varyheader when needed to address htmx caching issue - extractors
- Axum extractors for htmx request headers.
- guard
guards - Request guard for protecting a router against non-htmx requests.
- headers
- HTTP headers used by htmx.
- responders
- Axum responses for htmx response headers.
Structs§
- Auto
Vary Layer auto-vary - Addresses htmx caching issues
by automatically adding a corresponding
Varyheader whenHxRequest,HxTarget,HxTrigger,HxTriggerNameor their combination is used. - Auto
Vary Middleware auto-vary - Tower service for
AutoVaryLayer - HxBoosted
- The
HX-Boostedheader. - HxCurrent
Url - The
HX-Current-Urlheader. - HxEvent
- Represents a client-side event carrying optional data.
- HxHistory
Restore Request - The
HX-History-Restore-Requestheader. - HxLocation
- The
HX-Locationheader. - HxPrompt
- The
HX-Promptheader. - HxPush
Url - The
HX-Push-Urlheader. - HxRedirect
- The
HX-Redirectheader. - HxRefresh
- The
HX-Refreshheader. - HxReplace
Url - The
HX-Replace-Urlheader. - HxRequest
- The
HX-Requestheader. - HxRequest
Guard guards - Tower service that implements redirecting to non-partial routes.
- HxRequest
Guard Layer guards - Checks if the request contains the
HX-Requestheader, redirecting to the given location if not. - HxReselect
- The
HX-Reselectheader. - HxResponse
Trigger - The
HX-Trigger*header. - HxReswap
- The
HX-Reswapheader. - HxRetarget
- The
HX-Retargetheader. - HxTarget
- The
HX-Targetheader. - HxTrigger
- The
HX-Triggerheader. - HxTrigger
Name - The
HX-Trigger-Nameheader. - Location
Options serde - More options for
HX-Locationheader. - Vary
HxRequest - The
Vary: HX-Requestheader. - Vary
HxTarget - The
Vary: HX-Targetheader. - Vary
HxTrigger - The
Vary: HX-Triggerheader. - Vary
HxTrigger Name - The
Vary: HX-Trigger-Nameheader.
Enums§
- HxError
- Swap
Option - Values of the
hx-swapattribute. - Trigger
Mode - Describes when should event be triggered.
Constants§
- HX_
BOOSTED - Indicates that the request is via an element using
hx-boostattribute. - HX_
CURRENT_ URL - The current URL of the browser.
- HX_
HISTORY_ RESTORE_ REQUEST trueif the request is for history restoration after a miss in the local history cache.- HX_
LOCATION - Allows you to do a client-side redirect that does not do a full page reload.
- HX_
PROMPT - The user response to an
hx-prompt - HX_
PUSH_ URL - Pushes a new URL onto the history stack.
- HX_
REDIRECT - Can be used to do a client-side redirect to a new location.
- HX_
REFRESH - If set to
true, the client will do a full refresh on the page. - HX_
REPLACE_ URL - Replaces the currelt URL in the location bar.
- HX_
REQUEST - Always
true. - HX_
RESELECT - A CSS selector that allows you to choose which part of the response is used
to be swapped in. Overrides an existing
hx-selecton the triggering element - HX_
RESWAP - Allows you to specify how the response value will be swapped.
- HX_
RETARGET - A CSS selector that update the target of the content update to a different element on the page.
- HX_
TARGET - The
idof the target element, if it exists. - HX_
TRIGGER - Can be set as a request or response header.
- HX_
TRIGGER_ AFTER_ SETTLE - Allows you to trigger client-side events.
- HX_
TRIGGER_ AFTER_ SWAP - Allows you to trigger client-side events.
- HX_
TRIGGER_ NAME - The
nameof the triggered element, if it exists.