---
title: .stream()
description: 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()`](/guide/island/state). It just starts `undefined` and is fed by the generator instead of a fixed initial value.

```tsx twoslash
import { ilha } from "ilha";

const Live = ilha
  .stream("time", async function* ({ signal }) {
    while (!signal.aborted) {
      yield new Date().toISOString();
      await sleep(1000);
    }
  })
  .render(({ state }) => <p>{state.time()}</p>);

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
```

## How consumption differs by environment

The generator receives a single argument, `{ input, signal }`:

- `input` — resolved input props
- `signal` — aborts when the island unmounts; pass it to `fetch`, SSE layers, or `while` loops 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()`:

```tsx
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      |
