Optimistic Updates
Update regular and paginated query results immediately, then let Convex confirm or roll back the change.
An optimistic update changes local query results before the mutation response arrives. Convex rolls the overlay back when the mutation fails and reconciles it with confirmed server data when it succeeds.
Use optimism when the expected server result is predictable. Prefer the normal live update for complex authorization or server-generated values.
Regular query example
import type { Id } from '~/convex/_generated/dataModel'
import { api } from '#convex/api'
const addTodo = useConvexMutation(api.todos.create, {
optimisticUpdate: (store, args) => {
const optimisticTodo = {
_id: crypto.randomUUID() as Id<'todos'>,
_creationTime: Date.now(),
text: args.text,
completed: false,
}
updateQuery({
query: api.todos.list,
args: {},
store,
updater: (current) => [optimisticTodo, ...(current ?? [])],
})
},
})The optimistic item must contain every field the UI reads. Its ID is temporary and must not be used as a durable external identifier.
Regular helpers
| Helper | Use |
|---|---|
updateQuery | Derive one exact query result from its current value |
setQueryData | Replace one exact query result |
updateAllQueries | Update matching argument variants of one query |
deleteFromQuery | Remove matching items from an array result |
Exact query arguments matter. Updating { organizationId } does not update another subscribed argument set automatically.
Paginated example
const sendMessage = useConvexMutation(api.messages.send, {
optimisticUpdate: (store, args) => {
insertAtTop({
query: api.messages.list,
argsToMatch: { channelId: args.channelId },
store,
item: {
_id: crypto.randomUUID() as Id<'messages'>,
_creationTime: Date.now(),
channelId: args.channelId,
body: args.body,
},
})
},
})Paginated helpers
| Helper | Use |
|---|---|
insertAtTop | Add to the first visible position |
insertAtPosition | Add according to a Convex-value sort key |
insertAtBottomIfLoaded | Add at the end only when all pages are loaded |
updateInPaginatedQuery | Map matching loaded items |
deleteFromPaginatedQuery | Remove matching loaded items |
These helpers update loaded local pages. They do not create unloaded pages or change the backend query's ordering rules.
Failure and identity behavior
Convex owns rollback. Do not manually “undo” the same optimistic update in onError.
Identity changes retire the old client, including its optimistic state. A mutation that crossed the identity boundary may have committed remotely even though the local call rejects. Do not automatically replay it.