{"id":28146378,"url":"https://github.com/robertwayne/axum-htmx","last_synced_at":"2026-03-17T20:04:08.586Z","repository":{"id":183104284,"uuid":"669606319","full_name":"robertwayne/axum-htmx","owner":"robertwayne","description":"A set of htmx extractors, responders, and request guards for axum.","archived":false,"fork":false,"pushed_at":"2026-01-20T00:02:20.000Z","size":208,"stargazers_count":249,"open_issues_count":8,"forks_count":19,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-20T07:21:08.881Z","etag":null,"topics":["axum","htmx"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertwayne.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-07-22T20:37:24.000Z","updated_at":"2026-01-20T00:02:25.000Z","dependencies_parsed_at":"2023-07-22T21:47:10.203Z","dependency_job_id":"2654b05e-b574-4032-8b83-41b5723982c6","html_url":"https://github.com/robertwayne/axum-htmx","commit_stats":null,"previous_names":["robertwayne/axum-htmx"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/robertwayne/axum-htmx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertwayne%2Faxum-htmx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertwayne%2Faxum-htmx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertwayne%2Faxum-htmx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertwayne%2Faxum-htmx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertwayne","download_url":"https://codeload.github.com/robertwayne/axum-htmx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertwayne%2Faxum-htmx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30630037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T17:32:55.572Z","status":"ssl_error","status_checked_at":"2026-03-17T17:32:38.732Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["axum","htmx"],"created_at":"2025-05-14T23:12:31.659Z","updated_at":"2026-03-17T20:04:08.581Z","avatar_url":"https://github.com/robertwayne.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# axum-htmx\n\n\u003c!-- markdownlint-disable --\u003e\n\u003cdiv align=\"right\"\u003e\n\u003ca href=\"https://crates.io/crates/axum-htmx\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/v/axum-htmx?style=flat-square\" alt=\"crates.io badge\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://docs.rs/axum-htmx/latest/\"\u003e\n    \u003cimg src=\"https://img.shields.io/docsrs/axum-htmx?style=flat-square\" alt=\"docs.rs badge\"\u003e\n\u003c/a\u003e\n\u003c/div\u003e\n\u003cbr\u003e\n\u003c!-- markdownlint-enable --\u003e\n\n`axum-htmx` is a small extension library providing extractors, responders, and\n request guards for [htmx](https://htmx.org/) headers within\n [axum](https://github.com/tokio-rs/axum).\n\n## Table of Contents\n\n  - [Getting Started](#getting-started)\n  - [Extractors](#extractors)\n  - [Responders](#responders)\n    - [Vary Responders](#vary-responders)\n  - [Auto Caching Management](#auto-caching-management)\n  - [Request Guards](#request-guards)\n  - [Examples](#examples)\n    - [Example: Extractors](#example-extractors)\n    - [Example: Responders](#example-responders)\n    - [Example: Router Guard](#example-router-guard)\n  - [Feature Flags](#feature-flags)\n  - [Contributing](#contributing)\n    - [Testing](#testing)\n  - [License](#license)\n\n## Getting Started\n\nRun `cargo add axum-htmx` to add the library to your project.\n\n## Extractors\n\nAll of the [htmx request headers](https://htmx.org/reference/#request_headers)\nhave a supported extractor. Extractors are infallible, meaning they will always\nsucceed and never return an error. In the case where a header is not present,\nthe extractor will return `None` or `false` dependant on the expected return\ntype.\n\n| Header                       | Extractor                 | Value                     |\n|------------------------------|---------------------------|---------------------------|\n| `HX-Boosted`                 | `HxBoosted`               | `bool`                    |\n| `HX-Current-URL`             | `HxCurrentUrl`            | `Option\u003caxum::http::Uri\u003e` |\n| `HX-History-Restore-Request` | `HxHistoryRestoreRequest` | `bool`                    |\n| `HX-Prompt`                  | `HxPrompt`                | `Option\u003cString\u003e`          |\n| `HX-Request`                 | `HxRequest`               | `bool`                    |\n| `HX-Target`                  | `HxTarget`                | `Option\u003cString\u003e`          |\n| `HX-Trigger-Name`            | `HxTriggerName`           | `Option\u003cString\u003e`          |\n| `HX-Trigger`                 | `HxTrigger`               | `Option\u003cString\u003e`          |\n\n## Responders\n\nAll of the [htmx response headers](https://htmx.org/reference/#response_headers)\nhave a supported responder. A responder is a basic type that implements\n`IntoResponseParts`, allowing you to simply and safely apply the HX-* headers to\nany of your responses.\n\n| Header                    | Responder           | Value                               |\n|---------------------------|---------------------|-------------------------------------|\n| `HX-Location`             | `HxLocation`        | `String`                   |\n| `HX-Push-Url`             | `HxPushUrl`         | `String`                   |\n| `HX-Redirect`             | `HxRedirect`        | `String`                   |\n| `HX-Refresh`              | `HxRefresh`         | `bool`                              |\n| `HX-Replace-Url`          | `HxReplaceUrl`      | `String`                   |\n| `HX-Reswap`               | `HxReswap`          | `axum_htmx::responders::SwapOption` |\n| `HX-Retarget`             | `HxRetarget`        | `String`                            |\n| `HX-Reselect`             | `HxReselect`        | `String`                            |\n| `HX-Trigger`              | `HxResponseTrigger` | `axum_htmx::serde::HxEvent`         |\n| `HX-Trigger-After-Settle` | `HxResponseTrigger` | `axum_htmx::serde::HxEvent`         |\n| `HX-Trigger-After-Swap`   | `HxResponseTrigger` | `axum_htmx::serde::HxEvent`         |\n\n### Vary Responders\n\nAlso, there are corresponding cache-related headers, which you may want to add to\n`GET` responses, depending on the htmx headers.\n\n_For example, if your server renders the full HTML when the `HX-Request` header is\nmissing or `false`, and it renders a fragment of that HTML when `HX-Request: true`,\nyou need to add `Vary: HX-Request`. That causes the cache to be keyed based on a\ncomposite of the response URL and the `HX-Request` request header - rather than\nbeing based just on the response URL._\n\nRefer to [caching htmx docs section][htmx-caching] for details.\n\n| Header                  | Responder           |\n|-------------------------|---------------------|\n| `Vary: HX-Request`      | `VaryHxRequest`     |\n| `Vary: HX-Target`       | `VaryHxTarget`      |\n| `Vary: HX-Trigger`      | `VaryHxTrigger`     |\n| `Vary: HX-Trigger-Name` | `VaryHxTriggerName` |\n\nLook at the [Auto Caching Management](#auto-caching-management) section for\nautomatic `Vary` headers management.\n\n## Auto Caching Management\n\n__Requires feature `auto-vary`.__\n\nManual use of [Vary Reponders](#vary-responders) adds fragility to the code,\nbecause of the need to manually control correspondence between used extractors\nand the responders.\n\nWe provide a [middleware](crate::AutoVaryLayer) to address this issue by\nautomatically adding `Vary` headers when corresponding extractors are used.\nFor example, on extracting [`HxRequest`], the middleware automatically adds \n`Vary: hx-request` header to the response.\n\nLook at the usage [example][auto-vary-example].\n\n## Request Guards\n\n__Requires feature `guards`.__\n\nIn addition to the extractors, there is also a route-wide layer request guard\nfor the `HX-Request` header. This will redirect any requests without the header\nto \"/\" by default.\n\n_It should be noted that this is NOT a replacement for an auth guard. A user can\ntrivially set the `HX-Request` header themselves. This is merely a convenience\nfor preventing users from receiving partial responses without context. If you\nneed to secure an endpoint you should be using a proper auth system._\n\n## Examples\n\n### Example: Extractors\n\nIn this example, we'll look for the `HX-Boosted` header, which is set when\napplying the [hx-boost](https://htmx.org/attributes/hx-boost/) attribute to an\nelement. In our case, we'll use it to determine what kind of response we send.\n\nWhen is this useful? When using a templating engine, like\n[minijinja](https://github.com/mitsuhiko/minijinja), it is common to extend\ndifferent templates from a `_base.html` template. However, htmx works by sending\npartial responses, so extending our `_base.html` would result in lots of extra\ndata being sent over the wire.\n\nIf we wanted to swap between pages, we would need to support both full template\nresponses and partial responses _(as the page can be accessed directly or\nthrough a boosted anchor)_, so we look for the `HX-Boosted` header and extend\nfrom a `_partial.html` template instead.\n\n```rust\nuse axum::response::IntoResponse;\nuse axum_htmx::HxBoosted;\n\nasync fn get_index(HxBoosted(boosted): HxBoosted) -\u003e impl IntoResponse {\n    if boosted {\n        // Send a template extending from _partial.html\n    } else {\n        // Send a template extending from _base.html\n    }\n}\n```\n\n### Example: Responders\n\nWe can trigger any event being listened to by the DOM using an [htmx\ntrigger](https://htmx.org/attributes/hx-trigger/) header.\n\n```rust\nuse axum_htmx::HxResponseTrigger;\n\n// When we load our page, we will trigger any event listeners for \"my-event.\nasync fn index() -\u003e (HxResponseTrigger, \u0026'static str) {\n    // Note: As HxResponseTrigger only implements `IntoResponseParts`, we must\n    // return our trigger first here.\n    (\n        HxResponseTrigger::normal([\"my-event\", \"second-event\"]),\n        \"Hello, world!\",\n    )\n}\n```\n\n`htmx` also allows arbitrary data to be sent along with the event, which we can\nuse via the `serde` feature flag and the `HxEvent` type.\n\n```rust\nuse serde_json::json;\n\n// Note that we are using `HxResponseTrigger` from the `axum_htmx::serde` module\n// instead of the root module.\nuse axum_htmx::{HxEvent, HxResponseTrigger};\n\nasync fn index() -\u003e (HxResponseTrigger, \u0026'static str) {\n    let event = HxEvent::new_with_data(\n        \"my-event\",\n        // May be any object that implements `serde::Serialize`\n        json!({\"level\": \"info\", \"message\": {\n            \"title\": \"Hello, world!\",\n            \"body\": \"This is a test message.\",\n        }}),\n    )\n    .unwrap();\n\n    // Note: As HxResponseTrigger only implements `IntoResponseParts`, we must\n    // return our trigger first here.\n    (HxResponseTrigger::normal([event]), \"Hello, world!\")\n}\n```\n\n### Example: Router Guard\n\n```rust\nuse axum::Router;\nuse axum_htmx::HxRequestGuardLayer;\n\nfn router_one() -\u003e Router {\n    Router::new()\n        // Redirects to \"/\" if the HX-Request header is not present\n        .layer(HxRequestGuardLayer::default())\n}\n\nfn router_two() -\u003e Router {\n    Router::new()\n        .layer(HxRequestGuardLayer::new(\"/redirect-to-this-route\"))\n}\n```\n\n## Feature Flags\n\n\u003c!-- markdownlint-disable --\u003e\n| Flag        | Default  | Description                                                | Dependencies                                |\n|-------------|----------|------------------------------------------------------------|---------------------------------------------|\n| `auto-vary` | Disabled | A middleware to address [htmx caching issue][htmx-caching] | `futures`, `tokio`, `tower`                 |\n| `guards`    | Disabled | Adds request guard layers.                                 | `tower`, `futures-core`, `pin-project-lite` |\n| `serde`     | Disabled | Adds serde support for the `HxEvent` and `LocationOptions` | `serde`, `serde_json`                       |\n\u003c!-- markdownlint-enable --\u003e\n\n## Contributing\n\nContributions are always welcome! If you have an idea for a feature or find a\nbug, let me know. PR's are appreciated, but if it's not a small change, please\nopen an issue first so we're all on the same page!\n\n### Testing\n\n```sh\ncargo +nightly test --all-features\n```\n\n## License\n\n`axum-htmx` is dual-licensed under either\n\n- **[MIT License](/LICENSE-MIT)**\n- **[Apache License, Version 2.0](/LICENSE-APACHE)**\n\nat your option.\n\n[htmx-caching]: https://htmx.org/docs/#caching\n[auto-vary-example]: https://github.com/robertwayne/axum-htmx/blob/main/examples/auto-vary.rs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertwayne%2Faxum-htmx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertwayne%2Faxum-htmx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertwayne%2Faxum-htmx/lists"}