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.
Hooks
React hooks for data, connection state, and client access in the sync engine.
All hooks require a SyncProvider. They re-render automatically when data changes.
useModel
Reads a single model by ID. Suspends while bootstrapping or loading a lazy model.
import { Suspense } from "react";
import { useModel } from "@stratasync/react";
function TaskDetail({ taskId }: { taskId: string }) {
const task = useModel<Task>("Task", taskId);
if (!task) {
return <div>Task not found</div>;
}
return <div>{task.title}</div>;
}
// Wrap in Suspense boundary
function TaskPage({ taskId }: { taskId: string }) {
return (
<Suspense fallback={<div>Loading...</div>}>
<TaskDetail taskId={taskId} />
</Suspense>
);
}Signature: useModel<T>(modelName: string, id: string | null | undefined): T | null
| Parameter | Type | Description |
|---|---|---|
modelName | string | Registered model name. |
id | string | null | undefined | Model ID. Returns null immediately if null or undefined. |
Return value: T | null: the model instance or null if not found.
useModelState
Non-Suspense alternative to useModel. Returns explicit loading and error states.
import { useModelState } from "@stratasync/react";
function TaskDetail({ taskId }: { taskId: string }) {
const {
data: task,
isLoading,
isFound,
error,
refresh,
} = useModelState<Task>("Task", taskId);
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
if (!isFound) {
return <div>Task not found</div>;
}
return (
<div>
<h1>{task.title}</h1>
<button onClick={refresh}>Refresh</button>
</div>
);
}Signature: useModelState<T>(modelName: string, id: string | null | undefined): UseModelResult<T>
UseModelResult:
| Field | Type | Description |
|---|---|---|
data | T | null | The model instance. |
isLoading | boolean | Whether the model is being loaded. |
isFound | boolean | Whether the model was found (data !== null). |
error | Error | null | Any error that occurred during loading. |
refresh | () => Promise<void> | Manually re-fetch the model. |
useModelSuspense
Alias for useModel that makes the Suspense dependency explicit.
import { useModelSuspense } from "@stratasync/react";
function TaskDetail({ taskId }: { taskId: string }) {
const task = useModelSuspense<Task>("Task", taskId);
// Same behavior as useModel
}Signature: useModelSuspense<T>(modelName: string, id: string): T | null
useQuery
Queries models with filtering, sorting, and pagination. Returns loading states explicitly.
import { useQuery } from "@stratasync/react";
function TaskList({ projectId }: { projectId: string }) {
const {
data: tasks,
isLoading,
hasMore,
totalCount,
refresh,
} = useQuery<Task>("Task", {
where: (task) => task.projectId === projectId,
orderBy: (a, b) => a.createdAt - b.createdAt,
limit: 20,
});
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div>
<p>
Showing {tasks.length} of {totalCount} tasks
</p>
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
{hasMore && <button onClick={refresh}>Load more</button>}
</div>
);
}Signature: useQuery<T>(modelName: string, options?: UseQueryOptions<T>): UseQueryResult<T>
UseQueryOptions (extends QueryOptions):
| Option | Type | Default | Description |
|---|---|---|---|
where | (item: T) => boolean | - | Filter predicate. |
orderBy | (a: T, b: T) => number | - | Sort comparator. |
limit | number | - | Maximum results. |
offset | number | - | Results to skip. |
includeArchived | boolean | false | Include archived items. |
skip | boolean | false | Skip the query entirely. |
UseQueryResult:
| Field | Type | Description |
|---|---|---|
data | T[] | Query results. |
isLoading | boolean | Whether the query is executing. |
error | Error | null | Any error that occurred. |
totalCount | number | undefined | Total matching count before pagination. |
hasMore | boolean | Whether more results are available. |
refresh | () => Promise<void> | Re-execute the query. |
useQueryAll
Convenience wrapper around useQuery without pagination (limit/offset).
import { useQueryAll } from "@stratasync/react";
function AllTasks() {
const { data: tasks, isLoading } = useQueryAll<Task>("Task", {
where: (t) => t.status !== "done",
orderBy: (a, b) => a.title.localeCompare(b.title),
});
if (isLoading) {
return <div>Loading...</div>;
}
return <TaskGrid tasks={tasks} />;
}Signature: useQueryAll<T>(modelName: string, options?: Omit<UseQueryOptions<T>, "limit" | "offset">): UseQueryResult<T>
useQueryCount
Returns the count of matching models without fetching them.
import { useQueryCount } from "@stratasync/react";
function TaskCounter({ projectId }: { projectId: string }) {
const { count, isLoading } = useQueryCount<Task>(
"Task",
(task) => task.projectId === projectId && task.status === "todo"
);
if (isLoading) {
return <span>...</span>;
}
return <span>{count} tasks</span>;
}Signature: useQueryCount<T>(modelName: string, where?: (item: T) => boolean): { count: number; isLoading: boolean; error: Error | null }
| Field | Type | Description |
|---|---|---|
count | number | Number of matching models. |
isLoading | boolean | Whether the count is being computed. |
error | Error | null | Any error that occurred. |
useConnectionState
Returns sync status, last sync ID, pending count, and error state.
import { useConnectionState } from "@stratasync/react";
function SyncStatusBadge() {
const { status, lastSyncId, backlog, error } = useConnectionState();
if (error) {
return <Badge color="red">Error: {error.message}</Badge>;
}
if (status === "bootstrapping") {
return <Badge color="yellow">Bootstrapping...</Badge>;
}
if (status === "syncing") {
return (
<Badge color="green">
Synced (ID: {lastSyncId}) {backlog > 0 && `- ${backlog} pending`}
</Badge>
);
}
return <Badge color="gray">{status}</Badge>;
}Signature: useConnectionState(): UseConnectionStateResult
UseConnectionStateResult:
| Field | Type | Description |
|---|---|---|
status | SyncClientState | Current sync state: "disconnected", "connecting", "bootstrapping", "syncing", or "error". |
lastSyncId | number | Last sync ID received from the server. |
backlog | number | Number of pending (unsynced) transactions in the outbox. |
error | Error | null | Last sync error, or null if no error. |
useIsOffline
Returns true when the sync client's connection state is "disconnected".
import { useIsOffline } from "@stratasync/react";
function OfflineBanner() {
const isOffline = useIsOffline();
if (!isOffline) {
return null;
}
return <div>You're offline. Changes will sync when you reconnect.</div>;
}Signature: useIsOffline(): boolean
usePendingCount
Returns the count of unsynced transactions.
import { usePendingCount } from "@stratasync/react";
function PendingIndicator() {
const { count, hasPending } = usePendingCount();
if (!hasPending) {
return <span>All changes saved</span>;
}
return <span>{count} changes pending sync</span>;
}Signature: usePendingCount(): UsePendingCountResult
UsePendingCountResult:
| Field | Type | Description |
|---|---|---|
count | number | Number of pending transactions. |
hasPending | boolean | true when count > 0. |
useSync
Trigger a manual sync cycle.
import { useSync } from "@stratasync/react";
function SyncButton() {
const { sync, isSyncing } = useSync();
return (
<button onClick={sync} disabled={isSyncing}>
{isSyncing ? "Syncing..." : "Sync Now"}
</button>
);
}Signature: useSync(): { sync: () => Promise<void>; isSyncing: boolean }
| Field | Type | Description |
|---|---|---|
sync | () => Promise<void> | Triggers client.syncNow(). Guards against concurrent calls. |
isSyncing | boolean | true while a manual sync is in progress. |
useSyncClient
Returns the full sync context: client instance, connection state, and derived flags.
import { useSyncClient } from "@stratasync/react";
function SyncDebugPanel() {
const {
client,
state,
connectionState,
lastSyncId,
backlog,
error,
isReady,
isSyncing,
isOffline,
clientId,
} = useSyncClient();
return (
<pre>
{JSON.stringify(
{
state,
connectionState,
lastSyncId,
backlog,
isReady,
isOffline,
clientId,
},
null,
2
)}
</pre>
);
}Signature: useSyncClient(): SyncContextValue
SyncContextValue:
| Field | Type | Description |
|---|---|---|
client | SyncClient | The sync client instance. |
state | SyncClientState | Current sync state. |
connectionState | ConnectionState | Current connection state. |
lastSyncId | number | Last sync ID from server. |
backlog | number | Pending transaction count. |
error | Error | null | Last sync error. |
isReady | boolean | true when state is "syncing". |
isSyncing | boolean | true when state is "syncing" or "bootstrapping". |
isOffline | boolean | true when connection is "disconnected". |
clientId | string | Client instance identifier. |
Throws an error if called outside of a SyncProvider.
useSyncClientInstance
Returns the SyncClient instance for calling client methods directly.
import { useSyncClientInstance } from "@stratasync/react";
function CreateTaskButton() {
const client = useSyncClientInstance();
const handleCreate = async () => {
await client.create("Task", {
title: "New task",
status: "todo",
});
};
return <button onClick={handleCreate}>Create Task</button>;
}Signature: useSyncClientInstance(): SyncClient
useSyncReady
true when bootstrapping is complete and the client is in "syncing" state. Gate UI rendering until data is available.
import { useSyncReady } from "@stratasync/react";
function AppContent() {
const isReady = useSyncReady();
if (!isReady) {
return <FullPageLoader />;
}
return <Dashboard />;
}Signature: useSyncReady(): boolean
useSyncState
Returns the current SyncClientState string value.
import { useSyncState } from "@stratasync/react";
function StateIndicator() {
const state = useSyncState();
const labels: Record<string, string> = {
disconnected: "Disconnected",
connecting: "Connecting...",
bootstrapping: "Loading data...",
syncing: "Ready",
error: "Error",
};
return <span>{labels[state] ?? state}</span>;
}Signature: useSyncState(): SyncClientState
Returns one of: "disconnected", "connecting", "bootstrapping", "syncing", "error".