> ## Documentation Index
> Fetch the complete documentation index at: https://aerojs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Hypermedia

> Load HTML fragments from the server and swap them into the page with GET, POST, and lifecycle events.

Hypermedia adds server-driven partial updates: fetch an HTML fragment, swap it into a target, and optionally wire reactive bindings in the result. It complements static pages the same way HTMX does, but uses Aero action functions in `on:*` handlers instead of `hx-*` attributes.

<Note>
  Hypermedia defaults to `false`. Enable it only when you need fragment swaps. HTMX remains fully
  supported alongside Aero hypermedia.
</Note>

## Prerequisites

Fragment actions need something to call. For API routes that return HTML, enable Nitro first — see [Server](/getting-started/server).

For `busy` bindings and automatic binding processing after swaps, enable **both** flags:

```ts aero.config.ts theme={"theme":"github-dark-default"}
import { defineConfig } from '@aero-js/core/config'

export default defineConfig({
	reactivity: true,
	hypermedia: true,
	server: true,
})
```

## Basic fragment load

Page:

```html theme={"theme":"github-dark-default"}
<button on:click="{ GET('/api/panel', { target: '#panel' }) }">Load panel</button>
<div id="panel">Waiting…</div>
```

Handler (`server/api/panel.get.ts`):

```ts theme={"theme":"github-dark-default"}
import { defineHandler } from 'nitro/h3'

export default defineHandler(() => {
	return `<p class="secondary">Panel loaded from the server.</p>`
})
```

`GET`, `POST`, `PUT`, `PATCH`, and `DELETE` are available in `on:*` handlers when hypermedia is enabled — no import needed.

Call them from `<script is:state>` only after an explicit import:

```html theme={"theme":"github-dark-default"}
<script is:state>
	import { GET } from '@aero-js/hypermedia'

	const loadPanel = () => GET('/api/panel', { target: '#panel' })
</script>
```

Do not import these actions in `<script is:build>` — that is a compile error.

## Action methods

Each function takes a URL and an optional options object (see [action options](#action-options) and [swap modes](#swap)).

| Method   | Typical use                                              | Request body                                                                                  |
| -------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `GET`    | Load or refresh a fragment (panels, lists, detail views) | None                                                                                          |
| `POST`   | Create or submit data (forms, actions that change state) | Form fields when triggered from a `<form>`; otherwise empty unless you add a body via headers |
| `PUT`    | Replace a resource                                       | Same as `POST` when triggered from a form                                                     |
| `PATCH`  | Partial update                                           | Same as `POST` when triggered from a form                                                     |
| `DELETE` | Remove a resource                                        | Usually none                                                                                  |

**Load a panel** (`GET`):

```html theme={"theme":"github-dark-default"}
<button on:click="{ GET('/api/panel', { target: '#panel' }) }">Load panel</button>
<div id="panel">Waiting…</div>
```

**Submit a form** (`POST` — use `on:submit.prevent`):

```html theme={"theme":"github-dark-default"}
<form on:submit.prevent="{ POST('/api/contact', { target: '#status' }) }">
	<input name="email" type="email" required />
	<button type="submit">Send</button>
</form>
<div id="status"></div>
```

When the trigger is a `<form>`, field `name` values are serialized into the request body automatically.

**Update or delete** (`PUT`, `PATCH`, `DELETE`):

```html theme={"theme":"github-dark-default"}
<form on:submit.prevent="{ PUT('/api/items/1', { target: '#item' }) }">
	<input name="title" value="Updated title" />
	<button type="submit">Save</button>
</form>

<button on:click="{ DELETE('/api/items/1', { target: '#item', swap: 'remove' }) }">
	Delete
</button>
```

On `<form>` triggers, `PUT`, `PATCH`, and `DELETE` are sent as `POST` with a hidden `_method` field so servers that expect method override (common in Rails-style APIs) work without JavaScript.

### Progressive enhancement by method

| Method                   | Element    | No-JS fallback                              |
| ------------------------ | ---------- | ------------------------------------------- |
| `GET`                    | `<a>`      | Native `href` to the action URL             |
| `POST`                   | `<form>`   | Native `action` + `method="post"`           |
| `PUT`, `PATCH`, `DELETE` | `<form>`   | `POST` + hidden `_method` input             |
| `GET`                    | `<button>` | None — use `<a>` when fallback matters      |
| `DELETE`                 | `<button>` | None — use a `<form>` when fallback matters |

## Action options

Second argument to every action function: `GET('/api/url', { … })`.

| Option        | Type                | Default                                  | Effect                                                                                                                                                                                               |
| ------------- | ------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `target`      | `string`            | trigger element                          | CSS selector for swap target — [details below](#target)                                                                                                                                              |
| `swap`        | `string`            | `innerHTML`                              | How response HTML is inserted — [swap modes](#swap)                                                                                                                                                  |
| `headers`     | `object`            | —                                        | Extra fetch headers (for example `Accept: text/html`)                                                                                                                                                |
| `pushUrl`     | `boolean \| string` | —                                        | Update the address bar after success. `true` uses the link's `href` on `<a>` triggers; a string sets an explicit path. Set `false` to skip. Server can also send an `aero-push-url` response header. |
| `state`       | boolean signal      | —                                        | Toggle a `let` binding `true` while the request is in flight (requires reactivity). Same signal used by `busy`.                                                                                      |
| `autoDisable` | `boolean`           | `true` for `POST`/`PUT`/`PATCH`/`DELETE` | Disable the trigger element during the request. Set `false` to keep it clickable.                                                                                                                    |
| `ariaBusy`    | `boolean`           | `false`                                  | Set `aria-busy="true"` on trigger and target during the lifecycle                                                                                                                                    |

```html theme={"theme":"github-dark-default"}
<script is:state>
	let isSaving = false
</script>

<button
	busy="{ isSaving }"
	on:click="{ POST('/api/save', { target: '#result', state: isSaving, ariaBusy: true }) }">
	Save
</button>
```

## Swap target and mode

### `target`

CSS selector for the element that receives the response. Defaults to the element that triggered the action (the button, link, or form).

```html theme={"theme":"github-dark-default"}
<button on:click="{ GET('/api/panel', { target: '#panel' }) }">Load</button>
<div id="panel"></div>
```

### `swap`

How response HTML is inserted relative to the target. Names match [HTMX swap modes](https://htmx.org/attributes/hx-swap/) and map to `insertAdjacentHTML` / `innerHTML` behavior.

| Mode          | Effect                                                                                                                                       |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `innerHTML`   | Replace **inside** the target (default). Children are discarded; response becomes the new contents.                                          |
| `outerHTML`   | Replace the **target element itself** with the response. The target node is removed from the document.                                       |
| `beforebegin` | Insert the response **immediately before** the target, as a sibling.                                                                         |
| `afterbegin`  | Insert the response **inside** the target, before its first child.                                                                           |
| `beforeend`   | Insert the response **inside** the target, after its last child (append).                                                                    |
| `afterend`    | Insert the response **immediately after** the target, as a sibling.                                                                          |
| `replace`     | Same as `outerHTML` — replace the target element with the response.                                                                          |
| `remove`      | Remove the target from the document. Response HTML is ignored.                                                                               |
| `none`        | Do not change the DOM. Lifecycle events still run; use when the response is handled elsewhere (for example a client script reading headers). |

**Replace panel contents** (`innerHTML` — default):

```html theme={"theme":"github-dark-default"}
<button on:click="{ GET('/api/panel', { target: '#panel' }) }">Refresh</button>
<div id="panel">Loading…</div>
```

**Append a row** (`beforeend`):

```html theme={"theme":"github-dark-default"}
<button on:click="{ GET('/api/items/next', { target: '#list', swap: 'beforeend' }) }">
	Append item
</button>
<ul id="list"></ul>
```

**Replace a list item in place** (`outerHTML`):

```html theme={"theme":"github-dark-default"}
<button on:click="{ GET('/api/items/1', { target: '#item-1', swap: 'outerHTML' }) }">
	Refresh item
</button>
<li id="item-1">Old content</li>
```

**Dismiss a toast** (`remove`):

```html theme={"theme":"github-dark-default"}
<button on:click="{ POST('/api/dismiss', { target: '#toast', swap: 'remove' }) }">
	Dismiss
</button>
<div id="toast">Saved.</div>
```

## Loading state

Tie a boolean signal to request lifecycle with `busy` or the `state` action option (requires both reactivity and hypermedia):

```html theme={"theme":"github-dark-default"}
<script is:state>
	let isSaving = false
</script>

<button
	busy="{ isSaving }"
	on:click="{ POST('/api/save', { target: '#result', state: isSaving }) }">
	Save
</button>
```

## Progressive enhancement

For static `GET` on `<a>` and `POST` on `<form>`, the compiler emits native `href` / `action` when the URL is a string literal. JavaScript enhances those elements when hypermedia is active.

<Warning>
  On forms using `POST()`, add `on:submit.prevent` so the browser does not also submit the full
  document.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Hypermedia recipes" icon="book-open" href="/guide/hypermedia">
    Inline panels, partial form posts, loading UX, lifecycle hooks.
  </Card>

  <Card title="Using hypermedia with reactivity" icon="layers" href="/guide/hypermedia/using-with-reactivity">
    Reactive swapped fragments, `busy`, and shared page state.
  </Card>

  <Card title="Reactive bindings" icon="sparkles" href="/guide/reactivity/bindings">
    `busy`, events, and form bindings used with actions.
  </Card>

  <Card title="Server routes" icon="server" href="/getting-started/server">
    Nitro handlers that return HTML fragments.
  </Card>
</CardGroup>
