Lazy island hydration
A signals island can defer its hydration off the critical path. The server-rendered HTML is visually interactive right away; the runtime only attaches reactive bindings when it pays to — "visible" (when scrolled into view), "idle" (requestIdleCallback) or "interaction" (first pointer / key / focus). With no opts.lazy, the island hydrates eagerly — the default behavior is unchanged.
Output
Eager (default) — hydrated on load.
count: 1 (×2 = 2)
Idle — hydrated inside a requestIdleCallback, after first paint.
count: 4 (×2 = 8)
Interaction — hydrated on first pointer / key / focus.
count: 3 (×2 = 6)
Scroll down… ↓
count: 2 (×2 = 4)
Visible — hydrated when scrolled into view (IntersectionObserver). The counter above only wired up the moment you scrolled it on-screen.
API: signalsIsland(name, Component, props, opts?) where opts.lazy ∈ "visible" | "idle" | "interaction". Absent = eager.
import { signalsIsland } from "@bext-stack/framework/signals";
import Counter from "../../../components/Counter";
export default function Page() {
return (
<div>
{/* eager — hydrates on load (default, no opts) */}
{signalsIsland("Counter", Counter, { initial: 1 })}
{/* idle — hydrates in a requestIdleCallback, off the critical path */}
{signalsIsland("Counter", Counter, { initial: 4 }, { lazy: "idle" })}
{/* interaction — hydrates on first pointer / key / focus on the island */}
{signalsIsland("Counter", Counter, { initial: 3 }, { lazy: "interaction" })}
<div style="margin-top:120vh">
{/* visible — hydrates when scrolled into view (IntersectionObserver) */}
{signalsIsland("Counter", Counter, { initial: 2 }, { lazy: "visible" })}
</div>
</div>
);
}