status-badge Hex.pm Version Hex.pm License hexdocs.pm

Useful functions for Elixir applications using htmx.

Offers a Plug that performs a few tasks:

  • Parses HX-* headers into conn.assigns
  • Adds htmx to your cache-control headers
  • Controls the rendering of Phoenix layouts, hiding them for htmx requests while showing them for non-htmx requests.

Installation

This library is available in Hex, and the package can be installed by adding hypa to your list of dependencies in mix.exs:

def deps do
  [
    {:hypa, "~> 0.2.0"}
  ]
end

Documentation can be found at https://hexdocs.pm/hypa.

Usage

Add Hypa.Plug to your Plug pipeline like this:

plug Hypa.Plug

Request headers

This plug adds htmx's request headers to conn.assigns:

headerassign
HX-Boostedconn.assigns[:htmx][:boosted]
HX-Current-URLconn.assigns[:htmx][:current_url]
HX-History-Restore-Requestconn.assigns[:htmx][:history_restore_request]
HX-Promptconn.assigns[:htmx][:prompt]
HX-Requestconn.assigns[:htmx][:request]
HX-Targetconn.assigns[:htmx][:target]
HX-Trigger-Nameconn.assigns[:htmx][:trigger_name]
HX-Triggerconn.assigns[:htmx][:trigger]

If the request does not have htmx headers, then conn.assigns[:htmx] will not be present.

Response headers

This plug also adds a Vary response header to your conn containing HX-Request, because your server is likely rendering different content for htmx requests. This value is prepended to the values already in the Vary header, so it will not overwrite any values you've already put in there. If you want to add more values to the Vary header yourself, be sure to use Plug.Conn.prepend_resp_headers/2 unless you do intend to overwite the entire Vary value.

headervalue
varyhx-request

The Hypa.Conn module also contains functions for adding htmx response headers to your conn.

HeaderFunction
HX-LocationHypa.Conn.put_hx_location/2
HX-Push-UrlHypa.Conn.put_hx_push_url/2
HX-RedirectHypa.Conn.put_hx_redirect/2
HX-RefreshHypa.Conn.put_hx_refresh/2
HX-Replace-UrlHypa.Conn.put_hx_replace_url/2
HX-ReswapHypa.Conn.put_hx_reswap/2
HX-RetargetHypa.Conn.put_hx_retarget/2
HX-ReselectHypa.Conn.put_hx_reselect/2
HX-TriggerHypa.Conn.put_hx_trigger/2
HX-Trigger-After-SettleHypa.Conn.put_hx_trigger_after_settle/2
HX-Trigger-After-SwapHypa.Conn.put_hx_trigger_after_swap/2

Phoenix layouts

If your project is using Phoenix, Hypa.Plug will also disable Phoenix's root and app layouts when responding to htmx requests. If the htmx request is boosted or is a history restore request, however, the app layout will be enabled, since those requests expect an entire HTML body. The root layout is disabled for all htmx requests.

conditionroot layoutapp layout
HX-Request != "true"enabledenabled
HX-Boosted == "true"disabledenabled
HX-History-Restore-Request == "true"disabledenabled
HX-Request == "true"disableddisabled

Rendering multiple templates at once

If your project is using Phoenix, the Hypa.Phoenix module is available. It has the function render_many/2 which renders multiple templates and concatenates the result. This is useful for out-of-band swaps. Provide a list containing either template names, or tuples of template names and the assigns to give them.

conn
|> Hypa.Phoenix.render_many([
  :first,
  {:second, [a: 1]},
  {"third", [b: 2]}
])

License

Copyright © 2026 Rosa Richter

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.