Islands — overview
@atolljs/islands renders a framework tree inside a Atoll worker: the worker owns the render loop and every commit serializes to an op stream the main thread replays as real DOM mutations into your element. Opt-in DOM rendering off the main thread — the shell keeps events, layout, and slots; the worker keeps the app.
Vocabulary
| Term | Meaning |
|---|---|
| Island | The mounted unit — mountIsland() puts one instance of a registered app into a container element. Each mount mints an app@N instance key scoping its op queue, document, and events. |
| PolyWorker | definePolyWorker({ apps }) — one worker hosting a registry of named apps; several islands can share it. |
| MonoWorker | defineMonoWorker(app) — one worker pinned to a single app; own bundle, own failure domain. |
| Shell | Your main-thread app — plain DOM via mountIsland, or a framework via the *-island packages. |
Install, mounting, and the mount API live under Quickstart.
Live demo
Eight islands on a framework-free shell — plain mountIsland calls, no framework on the main thread at all. The React apps ride the registry worker script (data-table mounts twice — same app, separate workers), vue-notes runs a real Vue createRenderer, and the imperative islands get dedicated instance workers running a hand-written proxy-DOM app and unmodified Leaflet 1.9.
If the frame is blank, the examples aren't up — run npm run dev:all (dev servers) or npm run serve:all (built apps) from the repository root.
Which workloads belong in an island
Every event costs one postMessage round trip — pointermove, per-keystroke input, and scroll handlers re-render on worker latency, and preventDefault can't work (the real event already dispatched). Keep high-frequency input on the main thread — slots exist for exactly this — and put coarse interactions (clicks, toggles, form submits) behind the island. Main-thread replay scales with op count, not tree size, so fine-grained framework updates are cheap while whole-tree rebuilds cost an order of magnitude more per frame.