React
MelonDbProvider, useQuery, useWriter, and sync hooks.
@melon-db/db-react connects MelonDatabase to React with context and subscription-driven hooks.
Provider
Bootstrap the database once, then wrap your tree:
import { MelonDbProvider } from '@melon-db/db-react';
export function App({ db }: { db: MelonDatabase }) {
return (
<MelonDbProvider db={db}>
<TaskList />
</MelonDbProvider>
);
}For sync, nest MelonSyncProvider inside MelonDbProvider (see Sync frontend).
useQuery
Subscribes to a query AST or PreparedQuery and re-renders when underlying rows change:
import { createQueryFactory } from '@melon-db/db-query';
import { useQuery } from '@melon-db/db-react';
import { useMemo } from 'react';
function TaskList() {
const query = useMemo(
() =>
createQueryFactory(taskSchema)
.from<Task>('tasks')
.where('status', 'eq', 'open')
.orderBy('priority', 'desc')
.toAst(),
[],
);
const tasks = useQuery<Task>(query);
return tasks.map((t) => <TaskRow key={t.id} task={t} />);
}Options:
useQuery<Task>(query, {
enabled: true,
select: (rows) => rows.filter((t) => t.priority > 1),
});useQueryCount
const openCount = useQueryCount(openTasksCountAst);useFindMany / useFindFirst (Prisma args)
import { useFindMany, useFindFirst } from '@melon-db/db-react';
const tasks = useFindMany<Task>('tasks', {
where: { status: 'open' },
orderBy: { priority: 'desc' },
take: 20,
});Compiles Prisma-style args via @melon-db/db-prisma under the hood.
useFluentQuery
Pass a builder callback (same shape as collection.query((b) => …)):
import { useFluentQuery } from '@melon-db/db-react';
const tasks = useFluentQuery<Task>('tasks', (b) =>
b.where('status', 'eq', 'open').orderBy('priority', 'desc').limit(20),
);useRecord
Subscribe to a single row by primary key:
import { useRecord } from '@melon-db/db-react';
function TaskDetail({ id }: { id: string }) {
const task = useRecord<Task>('tasks', id);
if (!task) return <Text>Loading…</Text>;
return <Text>{task.title}</Text>;
}Async state hooks (*State)
Array-returning hooks stay backward compatible. Use *State variants when you need loading or error UI:
import { useQueryState, useRecordState } from '@melon-db/db-react';
const listState = useQueryState<Task>(openTasksAst);
const recordState = useRecordState<Task>('tasks', taskId);
if (listState.status === 'loading') return <Spinner />;
if (listState.status === 'error') return <ErrorView error={listState.error} />;
if (listState.status === 'ready') return <List data={listState.data} />;| Hook | Returns |
|---|---|
useQueryState | QueryAsyncState<T[]> |
useFindManyState | QueryAsyncState<T[]> |
useFluentQueryState | QueryAsyncState<T[]> |
useRecordState | QueryAsyncState<T | null> |
useMangoQuery
import { useMangoQuery } from '@melon-db/db-react';
const urgent = useMangoQuery<Task>('tasks', {
selector: { status: 'open', priority: { $gte: 3 } },
sort: [{ updatedAt: 'desc' }],
});useWriter
Stable callback wrapping db.write — use for mutations in components:
import { useWriter } from '@melon-db/db-react';
function AddTask() {
const write = useWriter();
async function onAdd(title: string) {
await write(async (tx) => {
await tx.collection('tasks').insert({
id: crypto.randomUUID(),
title,
status: 'open',
priority: 1,
updatedAt: new Date(),
});
});
}
// ...
}Other hooks
| Hook | Purpose |
|---|---|
useDatabase() | Access MelonDatabase from context |
useCollection(name) | Bound MelonCollection |
useFluentQuery | Fluent builder callback |
useRecord | Single record by id |
useQueryState / useFindManyState / useFluentQueryState / useRecordState | Loading + error discriminated union |
useSync() | Manual sync + status from MelonSyncProvider |
useSyncStatus() | Status-only subscription |
Example app
See apps/playground-rn/app/index.tsx — open tasks list with useQuery, useWriter, and useSync.
Related
- CRUD — underlying collection APIs
- Queries — building AST / Mango / Prisma inputs
- Read & write — why mutations go through
useWriter
