Islands
Islands are interactive components that hydrate independently. The rest of the page stays static HTML — zero JavaScript for non-interactive content.
# Creating an island
// src/islands/Counter.ts
import { html, signal } from "@deijose/nix-js";
export default function Counter({ initial }: { initial: number }) {
const count = signal(initial);
return html`
<div>
<button @click=${() => count.value--}>−</button>
<span>${() => count.value}</span>
<button @click=${() => count.value++}>+</button>
</div>
`;
}# Using an island in a page
import { island } from "@deijose/nix-js-kit";
import Counter from "../islands/Counter";
export default function HomePage() {
return html`
<article>
<h1>Counter Demo</h1>
${island("Counter", Counter, { initial: 0 }, "load")}
</article>
`;
}# island() signature
island(
name: string, // registry name (must match hydrateIslands key)
component: Function, // the island component
props?: object, // props passed to the component
directive?: "load" | "idle" | "visible", // hydration trigger
)# Hydration directives
| Directive | When it hydrates | Best for |
|---|---|---|
load |
Immediately | Above-the-fold interactivity |
idle |
requestIdleCallback |
Below-the-fold, non-critical |
visible |
IntersectionObserver |
Comments, widgets, heavy components |
// Hydrate immediately
island("Search", Search, {}, "load")
// Hydrate when browser is idle
island("Analytics", Analytics, {}, "idle")
// Hydrate when scrolled into view
island("Comments", Comments, { postId: "123" }, "visible")# Hydrating on the client
# Manual entry
// src/entry-client.ts
import { hydrateIslands } from "@deijose/nix-js-kit/island";
import Counter from "./islands/Counter";
hydrateIslands({ Counter });# Auto island scan
If you set islandsDir in your build config, nix-js-kit build scans src/islands/ and generates the entry for you:
await build({
appDir: "./src/app",
islandsDir: "./src/islands",
generatedEntry: "./.nix-js/entry-client.ts",
});The registry name is the path relative to islandsDir:
src/islands/Counter.ts→"Counter"src/islands/nav/MobileMenu.ts→"nav/MobileMenu"
# Island disposal
Before the SPA router swaps #app.innerHTML, old island effects are disposed via the __nix_js_island_dispose marker. This prevents leaked effects and stale DOM writes after navigation.
# Lower-level APIs
import { scanIslands, generateClientEntry } from "@deijose/nix-js-kit";
const islands = await scanIslands("./src/islands");
await generateClientEntry({ islands, outFile: "./.nix-js/entry-client.ts" });# Subpath export
import { hydrateIslands } from "@deijose/nix-js-kit/island";This subpath ensures the client bundle doesn't pull server-only code.