AI agents: fetch the documentation index at llms.txt. Markdown versions are available by appending .md to any page URL, including this page's markdown.
Server-side rendering and bootstrap
Prefetch data on the server, serialize snapshots, and hydrate the client for instant first paint.
Prefetch data on the server, embed it in the HTML, and seed client storage. The client starts its delta stream from where the server left off: no loading spinners.
Why bootstrap matters
Without SSR bootstrap, the browser shows a spinner while fetching from the sync API. With bootstrap, data is embedded in the HTML response: users see content immediately.
Without SSR bootstrap
sequenceDiagram participant Browser participant Server participant SyncAPI Browser->>Server: Request page Server-->>Browser: HTML (empty shell) Browser->>Browser: Mount React, show loading spinner Browser->>SyncAPI: Bootstrap request SyncAPI-->>Browser: NDJSON stream of model data Browser->>Browser: Populate stores, re-render with data
With SSR bootstrap
sequenceDiagram participant Browser participant Server participant SyncAPI Server->>SyncAPI: prefetchBootstrap (at request time) SyncAPI-->>Server: NDJSON stream Server->>Server: Serialize snapshot into page props Server-->>Browser: HTML with data embedded Browser->>Browser: Seed storage from snapshot Browser->>Browser: Create client and start sync Browser->>SyncAPI: Delta stream (catch up from lastSyncId)
Prefetching on the server
Call prefetchBootstrap in a Server Component or generateMetadata:
// app/tasks/task-list.tsx"use client";import { observer } from "mobx-react-lite";import { useQuery } from "@stratasync/react";export const TaskList = observer(function TaskList() { const { data: tasks, isLoading } = useQuery("Task", { orderBy: (a, b) => (b as Record<string, string>).createdAt.localeCompare( (a as Record<string, string>).createdAt ), }); if (isLoading) return <p>Loading...</p>; return ( <ul> {tasks.map((task) => { const t = task as Record<string, string>; return ( <li key={t.id}> <strong>{t.title}</strong> {t.status} </li> ); })} </ul> );});
The server prefetches and serializes, the client seeds IndexedDB before start, and NextSyncProvider calls client.start(). Because storage is pre-populated, hooks return data on the first render.
Incremental hydration
After seeding, the client opens a delta stream from lastSyncId. Changes between server render and client hydration arrive automatically, making the transition seamless.