Skip to content
Vincle

Loading…

    Components

    • <Defer>: defers content into a placeholder. It renders its own placeholder, so it is self-sufficient
    • <Slot>: a named placeholder, filled by a <Defer> that renders elsewhere in the tree

    <Defer>#

    Registers its children as deferred content and renders a placeholder in its place, showing fallback (empty when omitted) until the content is ready. Its id is generated, unless target names a <Slot> (below).

    import { Defer } from "@vincle/flow";
    <Defer fallback={<p>Loading dashboard…</p>}>
    <HeavyDashboard />
    </Defer>;

    Pass a factory to receive an AbortSignal. It fires when the client disconnects or the fragment's timeout elapses; forward it to your own cancelable work (e.g. fetch(url, { signal })) so that work stops early:

    <Defer fallback={<p>Loading dashboard…</p>}>{(signal) => <HeavyDashboard signal={signal} />}</Defer>

    The signal does not cut the render short: if it has fired by the time the render finishes, the result is discarded and routed to onError instead of being emitted.

    Merge types#

    merge controls how the content applies to the target element:

    merge Effect
    "replace" Target element is replaced (default)
    "append" Content inserted as last child of target
    "prepend" Content inserted as first child of target
    "before" Content inserted as previous sibling
    "after" Content inserted as next sibling
    "morph" Target is diffed against content in place

    "morph" keeps focus, scroll position and form state. Not every adapter supports every merge type: an unsupported one is rejected at registration with a clear error.

    <Slot>: named insertion point#

    A named placeholder for content deferred elsewhere in the tree: typically a layout reserving a spot, filled by a page component that cannot reach into that layout. Its children render immediately as placeholder content, until the matching <Defer> replaces them. name is required.

    When placeholder and content live in the same spot, skip Slot: Defer with its own fallback does the job.

    The <Slot> is the placeholder: its <Defer> emits none of its own, wherever the two sit in the page.

    import { Defer, Slot } from "@vincle/flow";
    import { Results } from "./results";
    function SearchPage() {
    return (
    <>
    <main>
    <h1>Search results</h1>
    <Slot name="results">
    <p>Loading results…</p>
    </Slot>
    </main>
    <Defer target="results">
    <Results />
    </Defer>
    </>
    );
    }

    Content types#

    Defer children (the DeferContent type) are a value, or a factory receiving the AbortSignal and returning one:

    Content Behaviour
    JSX.Element or string One patch, rendered once
    AsyncIterable<JSX.Element> A stream: one patch per item, as it comes

    The runtime consumes an AsyncIterable itself: no .then() or for await on your side. See Streaming.

    Props#

    Prop Applies to Meaning
    name Slot id of the placeholder (required)
    target Defer optional placeholder id; generated automatically when omitted
    fallback Defer placeholder content shown in the shell
    merge Defer how content applies to its target (default "replace")
    timeout Defer per-fragment render timeout in ms
    onError Defer per-fragment error handler, overriding the renderer's one