Streams
Paint Effect Streams of views, bridge atoms with Atom.toStream, and use when for per-emission generator bodies.
Streams
Generator components can yield a Stream of views. Compose with Effect (Stream.map, Stream.pipe, …) — Ilha repaints on each emission. SSR takes the first value only; the client keeps listening after mount.
import * as import StreamStream from "effect/Stream";
function* function Status(): Generator<Stream.Stream<View, never, never>, void, unknown>Status() {
yield import StreamStream.const map: <string, never, never, View>(self: Stream.Stream<string, never, never>, f: (a: string, i: number) => View) => Stream.Stream<View, never, never> (+1 overload)Transforms the elements of this stream using the supplied function.
**Example** (Mapping stream values)
```ts import.meta.vitest
import { Effect, Option, Stream } from "effect"
const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i))
await Effect.runPromise(Stream.runCollect(stream)) // => [1, 3, 5]
```map(
import StreamStream.const fromIterable: <string>(iterable: Iterable<string>, options?: {
readonly chunkSize?: number | undefined;
}) => Stream.Stream<string, never, never>
Creates a new `Stream` from an iterable collection of values.
**Details**
- `chunkSize`: Maximum number of values emitted per chunk.
**Example** (Creating a stream from an iterable)
```ts import.meta.vitest
import { Effect, Stream } from "effect"
const numbers = [1, 2, 3]
const program = Effect.gen(function*() {
const stream = Stream.fromIterable(numbers)
const values = yield* Stream.runCollect(stream)
values // => [1, 2, 3]
})
await Effect.runPromise(program)
```fromIterable(["idle", "ready"]),
(value: stringvalue) => <IntrinsicElements[string]: anyp>{value: stringvalue}</IntrinsicElements[string]: anyp>,
);
}
Async components can return a Stream directly — no generator required.
Atom.toStream
Turn an atom into a change feed with Atom.toStream:
import * as import StreamStream from "effect/Stream";
import * as import AtomAtom from "effect/unstable/reactivity/Atom";
import { const atom: AtomFnatom } from "ilha";
function* function List(): Generator<Stream.Stream<View, never, AtomRegistry>, void, unknown>List() {
const const items: AtomHandle<string[]>items = atom<string[]>(init: string[] | Atom.Atom<string[]> | Effect<string[], any, any> | Stream.Stream<string[], any, any>): AtomHandle<string[]>atom(["a", "b"]);
yield import StreamStream.const map: <string[], never, AtomRegistry, View>(self: Stream.Stream<string[], never, AtomRegistry>, f: (a: string[], i: number) => View) => Stream.Stream<View, never, AtomRegistry> (+1 overload)Transforms the elements of this stream using the supplied function.
**Example** (Mapping stream values)
```ts import.meta.vitest
import { Effect, Option, Stream } from "effect"
const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i))
await Effect.runPromise(Stream.runCollect(stream)) // => [1, 3, 5]
```map(import AtomAtom.const toStream: <string[]>(self: Atom.Atom<string[]>) => Stream.Stream<string[], never, AtomRegistry>Converts an atom into a stream using the `AtomRegistry` service.
**Details**
The stream emits the atom's current value immediately and then emits subsequent
changes until the stream scope is closed.toStream(const items: AtomHandle<string[]>items.AtomHandle<string[]>.atom: Atom.Atom<string[]>atom), (list: string[]list) => (
<IntrinsicElements[string]: anyul>
{list: string[]list.Array<string>.map<View>(callbackfn: (value: string, index: number, array: string[]) => View, thisArg?: any): View[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<IntrinsicElements[string]: anyli key: stringkey={item: stringitem}>{item: stringitem}</IntrinsicElements[string]: anyli>
))}
</IntrinsicElements[string]: anyul>
));
}
Use items().map(...) in a sync component for a simple list. Reach for Atom.toStream when a stream pipeline should drive updates (debounce, merge, server feeds).
when
when(stream, body) runs a generator body per emission. A new value interrupts the previous body — stale async work does not paint.
import * as import EffectEffect from "effect/Effect";
import * as import StreamStream from "effect/Stream";
import * as import AtomAtom from "effect/unstable/reactivity/Atom";
import { const atom: AtomFnatom, function when<A, E = never, R = never>(stream: Stream.Stream<A, E, R>, body: (value: A) => Generator<Yielded, View | void, unknown>): Instruction<void, E>when } from "ilha";
function* function Search(): Generator<View, void, any>Search() {
const const query: AtomHandle<string>query = atom<string>(init: string | Atom.Atom<string> | Effect.Effect<string, any, any> | Stream.Stream<string, any, any>): AtomHandle<string>atom("");
yield (
<IntrinsicElements[string]: anyinput
value: AtomHandle<string>value={const query: AtomHandle<string>query}
oninput: (e: Event) => voidoninput={(e: Evente: Event) =>
const query: AtomHandle<string>query.AtomHandle<string>.set(next: string): voidset((e: Evente.Event.currentTarget: EventTarget | nullThe **`currentTarget`** read-only property of the Event interface identifies the element to which the event handler has been attached.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget)
Alias for event.target.currentTarget as HTMLInputElement).HTMLInputElement.value: stringThe **`value`** property of the HTMLInputElement interface represents the current value of the <input> element as a string.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/value)value)
}
/>
);
yield* when<string, never, AtomRegistry>(stream: Stream.Stream<string, never, AtomRegistry>, body: (value: string) => Generator<Yielded, View | void, unknown>): Instruction<void, never>when(
import AtomAtom.const toStream: <string>(self: Atom.Atom<string>) => Stream.Stream<string, never, AtomRegistry>Converts an atom into a stream using the `AtomRegistry` service.
**Details**
The stream emits the atom's current value immediately and then emits subsequent
changes until the stream scope is closed.toStream(const query: AtomHandle<string>query.AtomHandle<string>.atom: Atom.Atom<string>atom).Pipeable.pipe<Stream.Stream<string, never, AtomRegistry>, Stream.Stream<string, never, AtomRegistry>>(this: Stream.Stream<string, never, AtomRegistry>, ab: (_: Stream.Stream<string, never, AtomRegistry>) => Stream.Stream<string, never, AtomRegistry>): Stream.Stream<string, never, AtomRegistry> (+21 overloads)pipe(
import StreamStream.const debounce: (duration: Input) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R> (+1 overload)Drops earlier elements within the debounce window and emits only the latest element after the pause.
**Example** (Debouncing stream elements)
```ts import.meta.vitest
import { Duration, Effect, Stream } from "effect"
const stream = Stream.make(1, 2, 3).pipe(Stream.debounce(Duration.zero))
const program = Effect.gen(function*() {
const values = yield* Stream.runCollect(stream)
values // => [ 3 ]
})
await Effect.runPromise(program)
```debounce("200 millis"),
),
function* (q: stringq) {
if (!q: stringq) {
yield <IntrinsicElements[string]: anyp>Type to search</IntrinsicElements[string]: anyp>;
return;
}
const const items: anyitems = yield* import EffectEffect.const tryPromise: <any, unknown>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<any>;
readonly catch: (error: unknown) => unknown;
} | ((signal: AbortSignal) => PromiseLike<any>)) => Effect.Effect<any, unknown, never>
Creates an `Effect` from an asynchronous computation that may throw or
reject, mapping failures into the error channel.
**When to use**
Use when you need to perform asynchronous operations that might fail, such
as fetching data from an API, and want thrown exceptions or rejected promises
captured as Effect errors.
**Details**
The promise thunk is evaluated when the effect runs. If it returns a promise
that resolves, the resolved value becomes the success value. If the thunk
throws before returning a promise, or if the returned promise rejects, the
thrown or rejected value is mapped into the error channel.
Passing the thunk directly maps failures to
{@link
Cause.UnknownError
}
.
Passing `{ try, catch }` uses `catch` to map failures to an error of type
`E`.
The thunk receives an `AbortSignal` that is aborted if the effect is
interrupted. The underlying asynchronous operation only stops if it observes
that signal.
**Gotchas**
If `catch` throws while mapping the error, that thrown value is treated as a
defect. Return the error value you want in the error channel instead of
throwing it.
**Example** (Wrapping a fetch request that may fail)
```ts import.meta.vitest
import { Effect } from "effect"
const getTodo = (id: number) =>
Effect.tryPromise(() => Promise.resolve({ id, completed: false }))
// ┌─── Effect<{ id: number; completed: boolean }, UnknownError, never>
// ▼
const program = getTodo(1)
await Effect.runPromise(program) // => { id: 1, completed: false }
```
**Example** (Mapping Promise rejections to a tagged error)
```ts import.meta.vitest
import { Data, Effect } from "effect"
class TodoFetchError extends Data.TaggedError("TodoFetchError")<{ readonly cause: unknown }> {}
const getTodo = (id: number) =>
Effect.tryPromise({
try: () => Promise.reject(`Todo ${id} is unavailable`),
// remap the error
catch: (cause) => new TodoFetchError({ cause })
})
// ┌─── Effect<never, TodoFetchError, never>
// ▼
const program = Effect.flip(getTodo(1))
const error = await Effect.runPromise(program)
error._tag // => "TodoFetchError"
```tryPromise({
try: (signal: AbortSignal) => PromiseLike<any>try: (signal: AbortSignalsignal) =>
function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+3 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)fetch(`/api/search?q=${function encodeURIComponent(uriComponent: string | number | boolean): stringEncodes a text string as a valid component of a Uniform Resource Identifier (URI).encodeURIComponent(q: stringq)}`, {
RequestInit.signal?: AbortSignal | null | undefinedAn AbortSignal to set request's signal.signal,
}).Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>Attaches callbacks for the resolution and/or rejection of the Promise.then((r: Responser) => r: Responser.Body.json(): Promise<any>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)json()),
catch: (error: unknown) => unknowncatch: (e: unknowne) => e: unknowne,
});
yield (
<IntrinsicElements[string]: anyul>
{(const items: anyitems as string[]).Array<string>.map<View>(callbackfn: (value: string, index: number, array: string[]) => View, thisArg?: any): View[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<IntrinsicElements[string]: anyli key: stringkey={item: stringitem}>{item: stringitem}</IntrinsicElements[string]: anyli>
))}
</IntrinsicElements[string]: anyul>
);
},
);
}
yield Stream.map(...) is enough for a stream of views. Use when when each emission needs its own async generator.
Generators
| Yield | Meaning |
|---|---|
yield <p>…</p> |
Paint this view |
yield stream |
Subscribe to a Stream of views |
yield* effect |
Run an Effect (fetch, sleep, Deferred) |
yield* when(...) |
Per-emission body; stale bodies interrupt |
Uncaught failures paint an error view.
Side effects
For atom-driven side effects in any component, use watch(). In generators, yield* Stream.runForEach works for finite streams:
import * as import EffectEffect from "effect/Effect";
import * as import StreamStream from "effect/Stream";
function* function Boot(): Generator<string | number | bigint | boolean | Effect.Effect<void, never, never> | VNode | AtomHandle<any> | Stream.Stream<View, any, any> | GeneratorFn | (() => View | void | Promise<View | void>) | Iterable<View> | null | undefined, void, any>Boot() {
yield* import StreamStream.const runForEach: <number, never, never, void, never, never>(self: Stream.Stream<number, never, never>, f: (a: number) => Effect.Effect<void, never, never>) => Effect.Effect<void, never, never> (+1 overload)Runs the provided effectful callback for each element of the stream.
**Example** (Running an effect for each value)
```ts import.meta.vitest
import { Effect, Stream } from "effect"
const stream = Stream.make(1, 2, 3)
const values: Array<string> = []
const program = Effect.gen(function*() {
yield* Stream.runForEach(stream, (n) => Effect.sync(() => values.push(`Processing: ${n}`)))
})
await Effect.runPromise(program)
values // => ["Processing: 1", "Processing: 2", "Processing: 3"]
```runForEach(
import StreamStream.const fromIterable: <number>(iterable: Iterable<number>, options?: {
readonly chunkSize?: number | undefined;
}) => Stream.Stream<number, never, never>
Creates a new `Stream` from an iterable collection of values.
**Details**
- `chunkSize`: Maximum number of values emitted per chunk.
**Example** (Creating a stream from an iterable)
```ts import.meta.vitest
import { Effect, Stream } from "effect"
const numbers = [1, 2, 3]
const program = Effect.gen(function*() {
const stream = Stream.fromIterable(numbers)
const values = yield* Stream.runCollect(stream)
values // => [1, 2, 3]
})
await Effect.runPromise(program)
```fromIterable([1, 2, 3]),
(n: numbern) => import EffectEffect.const sync: <void>(thunk: LazyArg<void>) => Effect.Effect<void, never, never>Creates an `Effect` that represents a synchronous side-effectful computation.
**When to use**
Use when you need to wrap a synchronous side-effectful operation that is not
expected to throw.
**Details**
The provided function is evaluated lazily when the effect runs.
**Gotchas**
The function must not throw. If it throws, the thrown value is treated as a
defect, not as a typed failure. Use `try` when throwing is expected.
**Example** (Capturing synchronous logging in an Effect)
```ts import.meta.vitest
import { Effect } from "effect"
const output: Array<unknown> = []
const log = (message: string) =>
Effect.sync(() => {
void output.push(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")
Effect.runSync(program)
output // => ["Hello, World!"]
```sync(() => var console: ConsoleThe `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.log(n: numbern)),
);
yield <IntrinsicElements[string]: anyp>Ready</IntrinsicElements[string]: anyp>;
}
Co-locate DOM effects in event handlers when you own the write site.
Deferred
Paint UI, wait for input, then continue:
import * as import DeferredDeferred from "effect/Deferred";
import * as import EffectEffect from "effect/Effect";
function* function Gate(): Generator<string | number | bigint | boolean | Effect.Effect<Deferred.Deferred<string, never>, never, never> | VNode | AtomHandle<any> | Stream<View, any, any> | GeneratorFn | (() => View | void | Promise<View | void>) | Iterable<View> | Effect.Effect<string, never, never> | null | undefined, void, any>Gate() {
const const deferred: Deferred.Deferred<string, never>deferred = yield* import DeferredDeferred.const make: <string, never>() => Effect.Effect<Deferred.Deferred<string, never>, never, never>Creates a new `Deferred`.
**When to use**
Use to allocate an empty `Deferred` inside an `Effect` workflow.
**Example** (Creating a Deferred)
```ts import.meta.vitest
import { Deferred, Effect } from "effect"
const program = Effect.gen(function*() {
const deferred = yield* Deferred.make<number>()
yield* Deferred.succeed(deferred, 42)
return yield* Deferred.await(deferred)
})
await Effect.runPromise(program) // => 42
```make<string>();
yield (
<IntrinsicElements[string]: anybutton
type: stringtype="button"
onclick: () => booleanonclick={() =>
import EffectEffect.const runSync: <boolean, never>(effect: Effect.Effect<boolean, never, never>) => booleanExecutes an effect synchronously and returns its success value.
**When to use**
Use when you need to execute an effect that is guaranteed to complete
synchronously.
**Details**
If the effect fails, dies, is interrupted, or performs asynchronous work,
`runSync` throws a `FiberFailure` instead of returning a value. Use
`runSyncExit` when you want the failure captured as an `Exit`.
**Example** (Running a synchronous effect)
```ts import.meta.vitest
import { Effect } from "effect"
const output: Array<unknown> = []
const program = Effect.sync(() => {
void output.push("Hello, World!")
return 1
})
const result = Effect.runSync(program)
void output.push(result)
output // => ["Hello, World!", 1]
```
**Example** (Throwing for failed or async effects)
```ts import.meta.vitest
import { Effect } from "effect"
const output: Array<unknown> = []
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
void output.push("failed effect")
}
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
void output.push("async effect")
}
output // => ["failed effect", "async effect"]
```runSync(import DeferredDeferred.const succeed: <string, never>(self: Deferred.Deferred<string, never>, value: string) => Effect.Effect<boolean> (+1 overload)Attempts to complete the `Deferred` with the specified value.
**When to use**
Use to complete a `Deferred` with a successful value.
**Details**
Fibers waiting on the `Deferred` receive the value only if this call
completes it. The returned effect succeeds with `true` when this call
completed the `Deferred`, or `false` if it was already completed.
**Example** (Completing a Deferred with a value)
```ts import.meta.vitest
import { Deferred, Effect } from "effect"
const program = Effect.gen(function*() {
const deferred = yield* Deferred.make<number>()
yield* Deferred.succeed(deferred, 42)
return yield* Deferred.await(deferred)
})
await Effect.runPromise(program) // => 42
```succeed(const deferred: Deferred.Deferred<string, never>deferred, "Ada"))
}
>
Continue
</IntrinsicElements[string]: anybutton>
);
const const name: stringname = yield* import DeferredDeferred.await<string, never>(self: Deferred.Deferred<string, never>): Effect.Effect<string, never, never>
export await
Retrieves the value of the `Deferred`, suspending the fiber running the
workflow until the result is available.
**When to use**
Use to wait for a `Deferred` to be completed and resume with its success,
failure, defect, or interruption.
**Details**
Awaiters observe the completion effect stored in the `Deferred`.
**Example** (Awaiting a Deferred value)
```ts import.meta.vitest
import { Deferred, Effect } from "effect"
const program = Effect.gen(function*() {
const deferred = yield* Deferred.make<number>()
yield* Deferred.succeed(deferred, 42)
return yield* Deferred.await(deferred)
})
await Effect.runPromise(program) // => 42
```await(const deferred: Deferred.Deferred<string, never>deferred);
yield <IntrinsicElements[string]: anyp>Hello, {const name: stringname}</IntrinsicElements[string]: anyp>;
}
Server feeds
Map a server stream to JSX with Stream.fromAsyncIterable. SSR serializes the first value; the client resumes the feed. See Server islands and PubSub state.