SERVER

Islands

The server sends static HTML. Interactive pieces — islands — mount themselves on the client and build their own DOM. That is the whole model: Astro's shape, with nothing to re-attach, because compiled AmoJS is the vanilla code you would have written and there is no framework runtime waiting to take over a server-rendered tree.

An island is a module and a container

TS
// src/islands/cart.ts — compiled for the browser; the page itself ships no JavaScript
import { signal, html, mount } from '@amojs.dev/core';

const Cart = (stock: number) => {
  const qty = signal(1);
  return html`<button onclick=${() => (qty.value = Math.min(stock, qty.value + 1))}>add (${qty})</button>`;
};

const host = document.getElementById('cart');
if (host) mount(() => Cart(Number(host.dataset.stock)), host);
in the page
<div id="cart" data-stock="${String(product.stock)}" style="min-height: 40px"></div>
<script type="module" src="/islands/cart.js"></script>

The container is the island's only contract with the page: an id to find, data-* attributes to read. Give it intrinsic size — it is empty until the module runs.

Self-contained by construction

The build compiles src/islands/ for the browser, copies core's prebuilt bundle to dist/_amo/, and rewrites each island's core import to reach it relatively. No import map, nothing to copy by hand, and exactly one core url for every island on the page — core keeps module-level state, so two copies would not interoperate.

The honest cost

An island's markup is built twice: once on the server for first paint, once on the client when it mounts. So keep islands small — the counter, the cart, the search box — and let the static content, which is most of a page, be rendered once. A page with no islands emits zero script bytes.

Islands are leaves

An island does not contain other islands or server-rendered children; it owns its subtree outright. Nesting is the tar pit every islands framework has fallen into, and the rule keeps the model trivially explainable.

A link is a real document load, as in any static site, so island state does not survive it. The browser's own back/forward cache usually restores the previous document intact — a dividend, not a guarantee. If you need an app whose state spans pages, that is a client-rendered app with the router, not a static site.

In amo dev

Editing an island re-imports and re-mounts it in place; its local state resets, and the old version's timers linger until a real reload — said plainly rather than pretended away. A css edit hot-swaps with state intact; a page edit reloads.