With htmx
htmx swaps server HTML into the page. Aegis makes parts of that HTML alive on the client. They are a good pair: htmx for navigation and fragments where the server can render every state, Aegis islands for the pieces that need client state, validation or optimistic updates. A swap does not break an island, and an island that arrives with a swap starts by itself.
Islands inside swapped fragments#
<button hx-get="/cart/items" hx-target="#cart" hx-swap="innerHTML">Refresh</button>
<div id="cart"></div>
<script type="module">
import { island, hydrate } from 'aegis';
island('qty', ({ props, signal, html }) => {
const qty = signal(props.qty);
return html`<input type="number" bind:value=${qty}> ${() => qty.value * props.price} €`;
}, { types: { qty: Number, price: Number } });
hydrate(document, { watch: true }); // every fragment htmx inserts is scanned; islands mount, removed ones are destroyed
</script>The server returns <div data-aegis="qty" data-qty="2" data-price="9">…</div> inside the fragment; hydrate(document, { watch: true }) sees it arrive through a MutationObserver and mounts it. When htmx removes the fragment, the island's scope is disposed: listeners, timers and requests go with it.
Without watch, call hydrate(evt.detail.target) in htmx's htmx:afterSwap event.
Morph instead of replace#
hx-swap="innerHTML" replaces the nodes, so an island in the target is destroyed and recreated. When the fragment contains state you want to keep (an open dropdown, a focused input, a running island), use Aegis's own swap with morph for that target:
import { swap } from 'aegis';
swap(document.querySelector('#cart'), await fetch('/cart/items'), { mode: 'morph' }); // diffs and patches; islands and focus surviveOr keep htmx and add the idiomorph extension; Aegis islands survive a morph either way because their host element is kept.
What htmx does that Aegis does not replace#
- Server-driven navigation with
hx-boost:boost()in Aegis does the same job, but if htmx already drives the page, keep it. - Request attributes on plain elements (
hx-geton a link): Aegis has no attribute-driven requests; it hasresource()andmutation()in components.
What Aegis adds to an htmx page#
- Forms with client validation and 422 mapping:
wireForm(form)on the server-rendered form; the same form still submits without JavaScript, and htmx can still drive the submit. - Optimistic updates: a like button, a quantity field, a reorder, without a round-trip before the UI moves.
- Client state that the server should not render: a filter with 10 000 rows already on the page, a wizard's draft, a chart.