Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. For this purpose, TanStack Query exports a useMutation hook.
Here's an example of a mutation that adds a new todo to the server:
<script setup>
import { useMutation } from '@tanstack/vue-query'
const { isPending, isError, error, isSuccess, mutate } = useMutation({
mutationFn: (newTodo) => axios.post('/todos', newTodo),
})
function addTodo() {
mutate({ id: new Date(), title: 'Do Laundry' })
}
</script>
<template>
<span v-if="isPending">Adding todo...</span>
<span v-else-if="isError">An error occurred: {{ error.message }}</span>
<span v-else-if="isSuccess">Todo added!</span>
<button @click="addTodo">Create Todo</button>
</template>
<script setup>
import { useMutation } from '@tanstack/vue-query'
const { isPending, isError, error, isSuccess, mutate } = useMutation({
mutationFn: (newTodo) => axios.post('/todos', newTodo),
})
function addTodo() {
mutate({ id: new Date(), title: 'Do Laundry' })
}
</script>
<template>
<span v-if="isPending">Adding todo...</span>
<span v-else-if="isError">An error occurred: {{ error.message }}</span>
<span v-else-if="isSuccess">Todo added!</span>
<button @click="addTodo">Create Todo</button>
</template>
A mutation can only be in one of the following states at any given moment:
Beyond those primary states, more information is available depending on the state of the mutation:
In the example above, you also saw that you can pass variables to your mutations function by calling the mutate function with a single variable or object.
Even with just variables, mutations aren't all that special, but when used with the onSuccess option, the Query Client's invalidateQueries method and the Query Client's setQueryData method, mutations become a very powerful tool.
It's sometimes the case that you need to clear the error or data of a mutation request. To do this, you can use the reset function to handle this:
<script>
import { useMutation } from '@tanstack/vue-query'
const { error, mutate, reset } = useMutation({
mutationFn: (newTodo) => axios.post('/todos', newTodo),
})
function addTodo() {
mutate({ id: new Date(), title: 'Do Laundry' })
}
</script>
<template>
<span v-else-if="error">
<span>An error occurred: {{ error.message }}</span>
<button @click="reset">Reset error</button>
</span>
<button @click="addTodo">Create Todo</button>
</template>
<script>
import { useMutation } from '@tanstack/vue-query'
const { error, mutate, reset } = useMutation({
mutationFn: (newTodo) => axios.post('/todos', newTodo),
})
function addTodo() {
mutate({ id: new Date(), title: 'Do Laundry' })
}
</script>
<template>
<span v-else-if="error">
<span>An error occurred: {{ error.message }}</span>
<button @click="reset">Reset error</button>
</span>
<button @click="addTodo">Create Todo</button>
</template>
useMutation comes with some helper options that allow quick and easy side-effects at any stage during the mutation lifecycle. These come in handy for both invalidating and refetching queries after mutations and even optimistic updates
useMutation({
mutationFn: addTodo,
onMutate: (variables) => {
// A mutation is about to happen!
// Optionally return a context containing data to use when for example rolling back
return { id: 1 }
},
onError: (error, variables, context) => {
// An error happened!
console.log(`rolling back optimistic update with id ${context.id}`)
},
onSuccess: (data, variables, context) => {
// Boom baby!
},
onSettled: (data, error, variables, context) => {
// Error or success... doesn't matter!
},
})
useMutation({
mutationFn: addTodo,
onMutate: (variables) => {
// A mutation is about to happen!
// Optionally return a context containing data to use when for example rolling back
return { id: 1 }
},
onError: (error, variables, context) => {
// An error happened!
console.log(`rolling back optimistic update with id ${context.id}`)
},
onSuccess: (data, variables, context) => {
// Boom baby!
},
onSettled: (data, error, variables, context) => {
// Error or success... doesn't matter!
},
})
When returning a promise in any of the callback functions it will first be awaited before the next callback is called:
useMutation({
mutationFn: addTodo,
onSuccess: async () => {
console.log("I'm first!")
},
onSettled: async () => {
console.log("I'm second!")
},
})
useMutation({
mutationFn: addTodo,
onSuccess: async () => {
console.log("I'm first!")
},
onSettled: async () => {
console.log("I'm second!")
},
})
You might find that you want to trigger additional callbacks beyond the ones defined on useMutation when calling mutate. This can be used to trigger component-specific side effects. To do that, you can provide any of the same callback options to the `