Skip to content
Vincle

Loading…

    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#

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

    Same arguments as 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#

    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.