Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/acknowledged-optimistic-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@tanstack/db': minor
---

Expose an `acknowledged` state for optimistic mutations, a virtual prop derived from existing internal state that sits between the optimistic write and the settled (synced-back) state.
Collections can use `tx.acknowledge()` from inside the mutation handler without exiting, so that people using the collection can decide to fire a transition or drop pending state upon `isAcknowledged` instead of waiting for `isSettled`.

Additive and non-breaking; `isPersisted` / `$synced` are unchanged; no conflict with pending/planned behaviors for `isSettled`.

- `Transaction.acknowledge()` — a setter called by a collection adapter when the server confirms a write.
- `Transaction.isAcknowledged` — resolves when `acknowledge()` is called, or with `isPersisted` when no adapter calls `acknowledge()`; rejects on failure. Never resolves later than `isPersisted`.
- `$acknowledged` virtual property — `true` once acknowledged, always `true` when `$synced` is `true`. Wired through row enrichment, the virtual-prop cache, and group-by aggregation, and emitted as a virtual-prop-only update when it flips mid-flight.
1 change: 1 addition & 0 deletions docs/guides/live-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The result types are automatically inferred from your query structure, providing
Live query results include computed, read-only virtual properties on every row:

- `$synced`: `true` when the row is confirmed by sync; `false` when it is still optimistic.
- `$acknowledged`: `false` at the start of an optimistic update; flips to `true` when `$synced` is flipped, or earlier if the mutation handler calls `tx.acknowledge()`
- `$origin`: `"local"` if the last confirmed change came from this client, otherwise `"remote"`.
- `$key`: the row key for the result.
- `$collectionId`: the source collection ID.
Expand Down
21 changes: 21 additions & 0 deletions docs/guides/mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,27 @@ tx.isPersisted.promise.then(() => {
console.log(tx.state) // 'pending', 'persisting', 'completed', or 'failed'
```

### Reacting to acknowledgement vs. sync

When a mutation handler acknowledges a server write before the sync comes back, the UI can choose to fire transitions or drop state based on this signal, either `transaction.isAcknowledged` or the `row.$acknowledged`

```typescript
const tx = todoCollection.insert(draft)

await tx.isAcknowledged.promise // resolves at acknowledgement — never later than isPersisted
toast.success("Profile created") // server has the write now; sync may still be catching up
router.navigate('/welcome')
```

Reactively, `$acknowledged` lets you show an intermediate "saved, syncing…" state:

```tsx
const rowState =
!row.$acknowledged ? 'pending-spinner' // in flight
: !row.$synced ? 'saved-unsettled' // server has it; sync catching up
: 'saved' // fully settled
```

Comment thread
mhsnook marked this conversation as resolved.
## Paced Mutations

Paced mutations provide fine-grained control over **when and how** mutations are persisted to your backend. Instead of persisting every mutation immediately, you can use timing strategies to batch, delay, or queue mutations based on your application's needs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ const stripVirtualProps = <T extends Record<string, any> | undefined>(
if (!value || typeof value !== `object`) return value
const {
$synced: _synced,
$acknowledged: _acknowledged,
$origin: _origin,
$key: _key,
$collectionId: _collectionId,
Expand Down
Loading