.stream()
Feed a state key from an async or sync generator. On the server ilha pulls only the first value; on the client it consumes the generator continuously.
.stream() declares a state key whose values come from a generator instead of a static initializer. It pairs well with server islands, live data, or any source you consume incrementally.
A streamed key is a state key: read and write it with the same signal accessor surface, and it participates in snapshots exactly like state(). It just starts undefined and is fed by the generator instead of a fixed initial value.
import { const ilha: RootBuilder & DirectIslandFactory & {
html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
raw: (value: string) => RawHtml;
mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
... 6 more ...;
onUncaughtError: typeof onUncaughtError;
}
ilha } from "ilha";
const const Live: Island<RootInput, MergeState<RootState, "time", string>>Live = const ilha: RootBuilder & DirectIslandFactory & {
html: (strings: TemplateStringsArray, ...values: unknown[]) => RawHtml;
raw: (value: string) => RawHtml;
mount: (registry: IslandRegistry, options?: MountOptions) => MountResult;
from: <TInput, TStateMap extends Record<string, unknown>>(selector: string | Element, island: Island<TInput, TStateMap>, props?: Partial<TInput>) => (() => void) | null;
... 6 more ...;
onUncaughtError: typeof onUncaughtError;
}
ilha
.IlhaBuilder<RootInput, RootState, RootDerived, RootActions>.stream<"time", string>(key: "time", fn: StreamFn<RootInput, string>): IlhaBuilder<RootInput, MergeState<RootState, "time", string>, RootDerived, RootActions>stream("time", async function* ({ signal: AbortSignalAborts when the island unmounts. Pass through to fetch/SSE layers.signal }) {
while (!signal: AbortSignalAborts when the island unmounts. Pass through to fetch/SSE layers.signal.AbortSignal.aborted: booleanThe **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (true) or not (false).
[MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted)aborted) {
yield new var Date: DateConstructor
new () => Date (+4 overloads)
Date().Date.toISOString(): stringReturns a date as a string value in ISO format.toISOString();
await function sleep(ms: number): Promise<unknown>sleep(1000);
}
})
.IlhaBuilder<RootInput, MergeState<RootState, "time", string>, RootDerived, RootActions>.render(fn: (ctx: RenderContext<RootInput, MergeState<RootState, "time", string>, RootDerived, RootActions>) => string | RawHtml): Island<RootInput, MergeState<RootState, "time", string>>render(({ state: IslandState<MergeState<RootState, "time", string>>state }) => <"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>{state: IslandState<MergeState<RootState, "time", string>>state.time: MarkedSignalAccessor
() => string (+1 overload)
time()}</"p": WithRawHtmlAttributeValues<JSX.HTMLAttributes<HTMLParagraphElement>>p>);
function function sleep(ms: number): Promise<unknown>sleep(ms: numberms: number) {
return new var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
Creates a new Promise.Promise((resolve: (value: unknown) => voidresolve) => function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout)setTimeout(resolve: (value: unknown) => voidresolve, ms: numberms));
}
How consumption differs by environment
The generator receives a single argument, { input, signal }:
input— resolved input propssignal— aborts when the island unmounts; pass it tofetch, SSE layers, orwhileloops to stop cleanly
On the server, ilha pulls only the first value so it can render inline — during SSR a streamed key renders its initial value. On the client, the generator is consumed continuously; each yielded value writes the state key and re-renders the island.
Declaring before render
Like other builder capabilities, declare .stream() before .render():
const Tasks = ilha
.stream("items", ({ signal }) => getTasks({ signal }))
.render(({ state }) => <ul>{/* … */}</ul>);
A stream that rejects during SSR degrades to its initial value instead of failing the render. On the client, an error from the generator is routed to the island error sink with source: "stream" unless it was cancelled by unmount.
Cleaning up
The unmount signal aborts when the island unmounts; ilha also aborts the stream controller itself. Cancel your while loop or in-flight fetch by checking signal.aborted so the generator can exit cleanly.
.stream() vs .derived() vs .effect()
.stream() |
.derived() |
.effect() |
|
|---|---|---|---|
| Produces | State values over time | One computed value | Side effects |
| SSR | First value only | Awaited in async SSR | Client-only |
| Re-runs | Per yield | On dependency change | On dependency change |
| Use when | Live/long-lived data | Derived computation | Imperative work |