One API for client
and server
Valet is a sync engine for React. Define your queries and mutations once, then run them locally for instant reads or on the server for full-dataset access.
Instant reads
Queries run against a local SQLite database in the browser. No network round-trip, no loading spinner. Try both — the Valet version responds instantly.
- Learn about Valet
- Define a schema
- Write queries
- Learn about Valet
- Define a schema
- Write queries
Works offline
Toggle sync off and make changes — they queue up locally. Toggle sync back on and everything catches up. Mutations replay deterministically.
- Buy groceries
- Walk the dog
- Ship feature
- Buy groceries
- Walk the dog
- Ship feature
Only sync what you need
The server database has all the rows. Sync filters control which ones reach each client. Try reassigning a task — it moves between devices instantly. No over-fetching, no client-side filtering.
- Review PR #42
- Update docs
- Review PR #42
- Fix login bug
- Update docs
- Deploy staging
- Fix login bug
- Deploy staging
Under the hood
Replay, not merge
Most sync engines merge values with last-write-wins. Valet replays each mutation against current server state so both changes are preserved.
Try it: click the buttons on both clients, then hit Reconnect to watch each operation replay on the server.
10
10
One API, two databases
Same function signature. Switch between client SQLite and your server DB with one line.
// Local — instant, offline
export const list = defineQuery({
execution: 'local',
handler: async (ctx) =>
ctx.db.query('todos').collect(),
})
// Server — full dataset access
export const search = defineQuery({
execution: 'server',
handler: async (ctx, args) =>
ctx.db.query('todos')
.filter(q => q.eq('title', args.term))
.collect(),
})
// Mutations work on both
export const create = defineMutation({
handler: async (ctx, args) => {
return ctx.db.insert('todos', {
title: args.title,
completed: false,
createdAt: Date.now(),
})
},
})Write normal JavaScript
crypto.randomUUID(), Math.random(), and Date.now() are automatically captured and replayed identically offline.
export const create = defineMutation({
handler: async (ctx, args) => {
const id = crypto.randomUUID()
const now = Date.now()
return ctx.db.insert('todos', {
title: args.title,
externalId: id,
createdAt: now,
})
},
})Scale-to-zero
Hosted by Expo. Idle projects shut down automatically. No always-on cost. Standard SQLite — your data is always portable.
One codebase. Every platform.
Built by Expo. First-class React Native support with native SQLite — the same hooks and queries, running natively on every platform.
How Valet compares
There are great tools in this space. Here's where Valet fits.
Local + server queries
Run queries instantly on a local SQLite replica, or on the server for full-dataset operations. Same defineQuery API — just change one line.
Offline-first with rebase
Mutations queue locally and replay against server state on reconnect. No last-write-wins data loss — every operation is preserved.
Deterministic replay
crypto.randomUUID(), Math.random(), and Date.now() are captured on first execution and replayed identically through offline sync and server re-execution.
No vendor lock-in
Standard SQLite on both client and server. Your data is always portable — no proprietary storage format, no proprietary query language.