Skip to main content

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

ts
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

HelperUse
updateQueryDerive one exact query result from its current value
setQueryDataReplace one exact query result
updateAllQueriesUpdate matching argument variants of one query
deleteFromQueryRemove matching items from an array result

Exact query arguments matter. Updating { organizationId } does not update another subscribed argument set automatically.

Paginated example

ts
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

HelperUse
insertAtTopAdd to the first visible position
insertAtPositionAdd according to a Convex-value sort key
insertAtBottomIfLoadedAdd at the end only when all pages are loaded
updateInPaginatedQueryMap matching loaded items
deleteFromPaginatedQueryRemove 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.