Skip to main content

Mutations

Execute transactional Convex writes with reactive state, callbacks, and safe results.

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 latest successful call
errorLatest committed ConvexCallError
reset()Clear data/error and return to idle

Throwing versus safe

Use the callable when failure is exceptional:

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

Use .safe() for form-style control flow:

ts
const result = await updateProject.safe({ projectId, name })

if (!result.ok) {
  formError.value = result.error
}

Callbacks

ts
const createProject = useConvexMutation(api.projects.create, {
  onSuccess: (projectId, args) => {
    navigateTo(`/projects/${projectId}`)
  },
  onError: (error, args) => {
    reportMutationFailure(args.name, error.kind)
  },
})

Callback exceptions are logged and ignored. They do not turn a successful server mutation into a failed mutation result.

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.

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