Skip to main content

Mutations

Execute transactional Convex writes with one direct callable and readonly state.

useConvexMutation returns a callable function with state attached.

ts
const updateProject = useConvexMutation(api.projects.update)

await updateProject({ projectId, name })

State

FieldMeaning
statusidle, pending, success, or error
pendingWhether the latest active call is pending
dataResult of the newest successful call
errorNewest committed ConvexCallError

Handle the rejected Promise

Use the callable when failure is exceptional:

ts
try {
  await updateProject({ projectId, name })
} catch (error) {
  toast.error(normalizeConvexError(error).message)
}

The same try/catch pattern is the form-control path. Run navigation, toast, or other follow-up work explicitly after await; the composable does not own unobserved success/error callbacks.

Live query updates

After the mutation commits, Convex updates affected query subscriptions. Do not call every query's refresh() as a standard mutation step.

Use an optimistic update only when waiting for the confirmed live result creates a meaningful UX problem.

Identity changes

Each call captures the current identity generation. If identity changes before it settles, the call rejects with IDENTITY_CHANGED and does not commit retained UI state under the new user.

When calls overlap, only the newest invocation owns data, error, status, and pending. Every invocation still resolves or rejects its own Promise.

The remote mutation may already have committed. Do not automatically retry an identity-changed write.