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
// 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);
<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.
Across navigations
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.