Skip to content

React and offline UX

ramose/react is a thin adapter over the framework-neutral client. It adds React subscriptions, not another cache, query language, or mutation state machine.

const tasks = useQuery(projectDb.query.from(Task).orderBy(Task.createdAt))

QueryState<A> is explicit:

type QueryState<A> =
| { status: "pending" }
| { status: "ready"; data: A }
| { status: "stale"; data: A }
| { status: "error"; error: unknown }

pending means there is no complete local answer yet. stale means a complete cached answer is available while resume is pending or the network is offline. Keep stale data visible and label connectivity; do not replace it with a full-screen spinner.

if (tasks.status === "pending") return <TaskListSkeleton />
if (tasks.status === "error") return <ErrorPanel error={tasks.error} />
return tasks.data.map((task) => (
<TaskRow
key={task.data.id}
task={task.data}
pending={task.local.pending}
onDone={(done) => task.mutate.setDone({ done })}
/>
))

Plain .data remains cloneable and free of methods or client metadata. A component rerenders when the entity data or its .local state changes.

const [receipt, setReceipt] = useState<Receipt | null>(null)
const state = useReceipt(receipt)
function save(title: string) {
setReceipt(task.mutate.rename({ title }))
}

Use entity pending state for lightweight row feedback and useReceipt when the UI needs exact queued, committed, rejected, output, or retry information. Disable destructive repeat actions while one receipt is in flight, but do not disable unrelated offline work.

const sync = useSyncState(client)

Connectivity is not query validity. A ready or stale query can be useful while offline. A connected client may still have a rejected receipt. Keep the two signals distinct in UI copy.

Hooks use React’s external-store contract, so snapshots are stable under concurrent rendering. Strict Mode does not duplicate activation, subscriptions, or operations. Unmounting a component removes its observer; the database in the session remains synchronized.

Server Components may render ordinary server-side queries, but browser database handles and offline replicas belong in Client Components. There is no persisted SSR query-result cache.