Components
Components for deferring content in the HTML shell. Imported from @vincle/flow.
Defer#
Renders a placeholder holding fallback, and patches it later with its
children:
import { Defer } from "@vincle/flow";
async function HeavyDashboard() { const data = await fetch("https://api.example.com/dashboard"); const json = await data.json(); return <pre>{JSON.stringify(json, null, 2)}</pre>;}
// fallback is shown in the shell immediately.<Defer target="dashboard" fallback={<p>Loading dashboard…</p>}> <HeavyDashboard /></Defer>;children is plain JSX, a promise, or an AsyncIterable (one patch per
item), or a factory (signal: AbortSignal) => … returning any of those. The
signal aborts when the request is cancelled or the timeout expires.
target is optional: without it, Vincle generates the placeholder id.
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
target |
string |
Generated id | Optional placeholder id; match a Slot name to fill it |
fallback |
JSX.Element |
N/A | Content rendered in the placeholder |
children |
JSX.Element | string | ((signal: AbortSignal) => JSX.Element) | AsyncIterable<JSX.Element> | ((signal: AbortSignal) => AsyncIterable<JSX.Element>) |
N/A | Content: plain JSX, a factory, or an async iterable |
merge |
"replace" | "append" | "prepend" | "before" | "after" | "morph" |
"replace" |
How content applies to the target |
timeout |
number |
N/A | Per-fragment render timeout in ms |
onError |
(error: unknown, info: FlowErrorInfo) => JSX.Element | void |
N/A | Per-fragment error handler |
Slot#
A placeholder declared apart from its Defer: typically a layout reserving a
spot that a page fills later, by matching target to name. Its children are
shown until the matching Defer patches them.
import { Slot, Defer } from "@vincle/flow";
async function LiveComments() { const res = await fetch("https://api.example.com/comments"); const comments: Array<{ id: string; text: string }> = await res.json(); return ( <ul> {comments.map((c) => ( <li key={c.id}>{c.text}</li> ))} </ul> );}
function Page() { return ( <html> <body> <Slot name="comments"> <p>Loading comments…</p> </Slot>
<Defer target="comments" timeout={5000}> <LiveComments /> </Defer> </body> </html> );}The Slot is the placeholder: its Defer emits none of its own, wherever the
two sit in the page.
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string |
N/A | Fragment id, required |
children |
VNode |
N/A | Fallback content rendered immediately in the shell |
Style / Script: asset emission#
<Style> and <Script> emit their tag where they stand, once per name: the
first occurrence wins, later ones render nothing.
import { Style, Script } from "@vincle/flow/components";
const Page = () => ( <> <Style name="page.css" media="screen"> {`.hero { color: red; }`} </Style> <Script name="page.js" defer src="/page.js" /> <main>…</main> </>);Style props:
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string |
N/A | Asset id, required, dedup key |
media |
string |
N/A | media attribute |
children |
string | (() => Awaitable<string>) |
N/A | CSS text, evaluated only if this occurrence emits |
Script props:
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string |
N/A | Asset id, required, dedup key |
src |
string |
N/A | src attribute |
module |
boolean |
false |
Sets type="module" |
defer |
boolean |
false |
Sets the defer attribute |
children |
string | (() => Awaitable<string>) |
N/A | Inline JS, evaluated only if this occurrence emits |
Fragment files written by emitFragments carry no <style>/<script>: the
shell that includes them already has the assets.