README.md

# Hypa

[![status-badge](https://ci.cosmicrose.dev/api/badges/17/status.svg)](https://ci.cosmicrose.dev/repos/17)
[![Hex.pm Version](https://img.shields.io/hexpm/v/hypa)](https://hex.pm/packages/hypa)
[![Hex.pm License](https://img.shields.io/hexpm/l/hypa)](https://hex.pm/packages/hypa)
[![hexdocs.pm](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/hypa/)

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.

[htmx]: https://htmx.org

## Installation

This library is [available in Hex](https://hex.pm/packages/hypa), and the package can be installed
by adding `hypa` to your list of dependencies in `mix.exs`:

```elixir
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:

[`Hypa.Plug`]: https://hexdocs.pm/hypa/Hypa.Plug.html

```elixir
plug Hypa.Plug
```

### Request headers

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

| header                       | assign                                          |
| ---                          | ---                                             |
| `HX-Boosted`                 | `conn.assigns[:htmx][:boosted]`                 |
| `HX-Current-URL`             | `conn.assigns[:htmx][:current_url]`             |
| `HX-History-Restore-Request` | `conn.assigns[:htmx][:history_restore_request]` |
| `HX-Prompt`                  | `conn.assigns[:htmx][:prompt]`                  |
| `HX-Request`                 | `conn.assigns[:htmx][:request]`                 |
| `HX-Target`                  | `conn.assigns[:htmx][:target]`                  |
| `HX-Trigger-Name`            | `conn.assigns[:htmx][:trigger_name]`            |
| `HX-Trigger`                 | `conn.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`](https://hexdocs.pm/plug/Plug.Conn.html#prepend_resp_headers/2)
unless you do intend to overwite the entire `Vary` value.

| header | value        |
| ---    | ---          |
| `vary` | `hx-request` |

### Phoenix layouts

If your project is using [Phoenix](https://phoenixframework.org), `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.

| condition                              | root layout | app layout |
| ---                                    | ---         | ---        |
| `HX-Request != "true"`                 | enabled     | enabled    |
| `HX-Boosted == "true"`                 | disabled    | enabled    |
| `HX-History-Restore-Request == "true"` | disabled    | enabled    |
| `HX-Request == "true"`                 | disabled    | disabled   |

## License

Copyright © 2025 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/>.