Mutations
Execute transactional Convex writes with one direct callable and readonly state.
useConvexMutation returns a callable function with state attached.
const updateProject = useConvexMutation(api.projects.update)
await updateProject({ projectId, name })State
| Field | Meaning |
|---|---|
status | idle, pending, success, or error |
pending | Whether the latest active call is pending |
data | Result of the newest successful call |
error | Newest committed ConvexCallError |
Handle the rejected Promise
Use the callable when failure is exceptional:
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.