Integration
The shell is instant. Slow parts wait.
Traditional SSR holds the entire response until every await resolves: the
slowest query sets your time-to-first-byte. Integration breaks that coupling.
You mark slow subtrees with <Defer>. The page shell ships
immediately. Fragments arrive later, each as soon as it resolves. No JavaScript
framework. No hydration. Just HTML in chunks.
How it works#
Page response
Shell: immediate
<nav>, <header>, layout…
Fragment: deferred
<Slot> placeholder
Fragment: deferred
<Slot> placeholder
Shell flushed → browser starts painting
Fragment A resolves → injected into placeholder
Fragment B resolves → injected independently
import { Slot, Defer, renderToStream } from "@vincle/flow";import { NativeAdapter } from "@vincle/flow/adapters";
declare function fetchComments(): Promise<{ text: string }[]>;
async function Comments() { const items = await fetchComments(); return ( <ul> {items.map((c) => ( <li>{c.text}</li> ))} </ul> );}
// <Slot> declares the placeholder with fallback content in the shell.// <Defer> pushes the real content, which replaces it when resolved.function Page() { return ( <html> <body> <h1>My page</h1> <Slot name="comments"> <p>Loading comments…</p> </Slot> <Defer target="comments">{() => <Comments />}</Defer> </body> </html> );}
// The shell is sent immediately; deferred fragments stream// in as they resolve. NativeAdapter injects a small polyfill.const stream = renderToStream(() => <Page />, NativeAdapter);Two rendering modes#
Streaming
Fragments arrive in the same HTTP response after the shell. One connection, progressive delivery.
renderToStream()
Static generation
Fragments are emitted as separate HTML files at build time. The browser fetches them on load.
renderToStatic()
Install#
npm install @vincle/flowbun add @vincle/flowAdapters#
An adapter controls how placeholders and patches are encoded in the HTML shell: for Turbo, HTMX, the browser's native partial updates, or a CDN's ESI. See Adapters for the five built-in ones and how to choose.