---
title: renderToFlowEvents
---

# renderToFlowEvents

The primitive under `renderToStream`: a `ReadableStream<FlowEvent>` of raw
events instead of adapter-encoded HTML, for a wire format the built-in adapters
don't cover.

## Signature

```tsx
import { renderToFlowEvents } from "@vincle/flow";

function renderToFlowEvents(
  node: () => JSX.Element,
  adapter: StreamingAdapter,
  opts?: FlowOptions & { mode?: "full" | "fragment" },
): ReadableStream<FlowEvent>;
```

Same arguments as [renderToStream](/api/flow/renderToStream); a non-streaming
adapter (`EsiAdapter`) is rejected at compile time and at runtime.

## Events

`FlowEvent` is a discriminated union:

| Event      | Shape                                                              | Emitted when                                                                                         |
| ---------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| `shell`    | `{ type: "shell"; html: string }`                                  | The page shell is fully rendered (one event)                                                         |
| `fragment` | `{ type: "fragment"; id: string; html: string; merge: MergeType }` | A deferred fragment is ready; `html` is the raw inner content, `merge` is that template's merge type |
| `close`    | `{ type: "close"; html: string }`                                  | The closing `</body></html>` tags, after all fragments                                               |

The shell is one event, emitted once fully rendered; fragments follow as they
resolve, one event each (one per yielded item for an `AsyncIterable`).
`mode: "fragment"` suppresses the `shell` and `close` events.

## Consuming the events

```tsx
import { renderToFlowEvents } from "@vincle/flow";
import { NativeAdapter } from "@vincle/flow/adapters";

const stream = renderToFlowEvents(() => <Page />, NativeAdapter);

for await (const ev of stream) {
  switch (ev.type) {
    case "shell":
      // ev.html: the full shell, before closing tags
      break;
    case "fragment":
      // ev.id, ev.html, ev.merge: patch into the shell yourself
      break;
    case "close":
      // ev.html: trailing </body></html>
      break;
  }
}
```

The stream handles backpressure; cancelling it (or aborting `opts.signal`)
aborts pending renders.
