A core library for creating fast optimistic updates with flexible backend support.
pnpm add @TanStack/optimistic
@TanStack/optimistic
provides a robust solution for managing data synchronization between your frontend application and backend services. It offers:
- Optimistic Updates: Apply changes instantly in the UI while syncing in the background
- Flexible Backend Support: Works with any backend or sync engine
- Immutable Snapshots: Create immutable snapshots of updates that can be persisted and rolled back
Collections are the central concept in @TanStack/optimistic
. A collection represents a set of data that can be synchronized, queried, and modified. Each collection:
- Has a unique identifier
- Contains data items
- Provides CRUD operations (insert, update, delete)
- Manages its own sync and persistence logic
All mutations in @TanStack/optimistic
are handled through transactions. Transactions:
- Group related changes together
- Track the state of mutations (pending, persisting, completed, failed)
- Support rollback in case of errors
- Provide optimistic updates to the UI
The library uses proxies to create immutable snapshots and track changes:
- Deep change tracking at any level of object nesting
- Special handling for various types (Date, RegExp, Map, Set)
- Circular reference handling with WeakMap cache
This is a core package that provides the fundamental optimistic update functionality. For most applications, you'll want to use this package with a framework-specific adapter:
-
@TanStack/react-optimistic
- React adapter with hooks for easy integration - Other framework adapters (coming soon)
The framework adapters provide idiomatic ways to use the core optimistic update functionality within your chosen framework.
// Insert a single item
insert({ text: "Buy groceries", completed: false })
// Insert multiple items
insert([
{ text: "Buy groceries", completed: false },
{ text: "Walk dog", completed: false },
])
// Insert with custom key
insert({ text: "Buy groceries" }, { key: "grocery-task" })
We use a proxy to capture updates as immutable draft optimistic updates.
// Update a single item
update(todo, (draft) => {
draft.completed = true
})
// Update multiple items
update([todo1, todo2], (drafts) => {
drafts.forEach((draft) => {
draft.completed = true
})
})
// Update with metadata
update(todo, { metadata: { reason: "user update" } }, (draft) => {
draft.text = "Updated text"
})
// Delete a single item
delete todo
// Delete multiple items
delete [todo1, todo2]
// Delete with metadata
delete (todo, { metadata: { reason: "completed" } })
Collections can optionally include a standard schema for data validation:
const todoCollection = createCollection({
id: "todos",
sync: {
/* sync config */
},
mutationFn: {
/* mutation functions */
},
schema: todoSchema, // Standard schema interface
})
The library includes a simple yet powerful transaction management system. Transactions are created using the createTransaction
function:
const tx = createTransaction({
mutationFn: async ({ transaction }) => {
// Implement your mutation logic here
// This function is called when the transaction is committed
},
})
// Apply mutations within the transaction
tx.mutate(() => {
// All collection operations (insert/update/delete) within this callback
// will be part of this transaction
})
Transactions progress through several states:
-
pending
: Initial state when a transaction is created -
persisting
: Transaction is being persisted to the backend -
completed
: Transaction has been successfully persisted -
failed
: An error was thrown while persisting or syncing back the Transaction