Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/TanStack/query/llms.txt

Use this file to discover all available pages before exploring further.

Perform mutations and side effects with the useMutation composable. It provides methods to trigger mutations and tracks their state reactively.

Signature

function useMutation<TData, TError, TVariables, TContext>(
  options: UseMutationOptions<TData, TError, TVariables, TContext>,
  queryClient?: QueryClient,
): UseMutationReturnType<TData, TError, TVariables, TContext>

Parameters

options
UseMutationOptions<TData, TError, TVariables, TContext>
required
Configuration options for the mutation. Can be a reactive ref or getter function.
queryClient
QueryClient
Custom QueryClient instance. If not provided, uses the client from context.

Returns

UseMutationReturnType<TData, TError, TVariables, TContext>
object
Reactive refs containing mutation state and methods.

Type Parameters

  • TData - Type of data returned by the mutation
  • TError - Type of error (defaults to DefaultError)
  • TVariables - Type of variables passed to the mutation (defaults to void)
  • TContext - Type of context returned by onMutate (defaults to unknown)

Examples

Basic Usage

<script setup>
import { useMutation, useQueryClient } from '@tanstack/vue-query'

const queryClient = useQueryClient()

const { mutate, isPending, isError, error } = useMutation({
  mutationFn: async (newTodo) => {
    const res = await fetch('/api/todos', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newTodo),
    })
    return res.json()
  },
  onSuccess: () => {
    // Invalidate and refetch
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
})

const addTodo = () => {
  mutate({ title: 'New Todo', completed: false })
}
</script>

<template>
  <div>
    <button @click="addTodo" :disabled="isPending">
      {{ isPending ? 'Adding...' : 'Add Todo' }}
    </button>
    <div v-if="isError">Error: {{ error.message }}</div>
  </div>
</template>

With TypeScript

<script setup lang="ts">
import { useMutation } from '@tanstack/vue-query'

interface Todo {
  id: number
  title: string
  completed: boolean
}

interface CreateTodoVariables {
  title: string
  completed: boolean
}

const { mutate } = useMutation({
  mutationFn: async (variables: CreateTodoVariables): Promise<Todo> => {
    const res = await fetch('/api/todos', {
      method: 'POST',
      body: JSON.stringify(variables),
    })
    return res.json()
  },
})

// Type-safe mutation call
mutate({ title: 'Learn Vue Query', completed: false })
</script>

Optimistic Updates

<script setup>
import { useMutation, useQueryClient } from '@tanstack/vue-query'

const queryClient = useQueryClient()

const { mutate } = useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    // Cancel outgoing refetches
    await queryClient.cancelQueries({ queryKey: ['todos'] })
    
    // Snapshot previous value
    const previousTodos = queryClient.getQueryData(['todos'])
    
    // Optimistically update cache
    queryClient.setQueryData(['todos'], (old) => {
      return old.map(todo => 
        todo.id === newTodo.id ? newTodo : todo
      )
    })
    
    // Return context with snapshot
    return { previousTodos }
  },
  onError: (err, newTodo, context) => {
    // Rollback on error
    queryClient.setQueryData(['todos'], context.previousTodos)
  },
  onSettled: () => {
    // Always refetch after error or success
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
})
</script>

Using mutateAsync

<script setup>
import { useMutation } from '@tanstack/vue-query'
import { ref } from 'vue'

const { mutateAsync, isPending } = useMutation({
  mutationFn: createTodo,
})

const handleSubmit = async () => {
  try {
    const newTodo = await mutateAsync({ title: 'New Todo' })
    console.log('Created todo:', newTodo)
    // Navigate or show success message
  } catch (error) {
    console.error('Failed to create todo:', error)
  }
}
</script>

Per-Mutation Callbacks

<script setup>
import { useMutation } from '@tanstack/vue-query'

const { mutate } = useMutation({
  mutationFn: createTodo,
})

// Pass callbacks per mutation call
const handleCreate = () => {
  mutate(
    { title: 'New Todo' },
    {
      onSuccess: (data) => {
        console.log('Created:', data)
      },
      onError: (error) => {
        console.error('Failed:', error)
      },
    }
  )
}
</script>

Reactive Mutation Options

<script setup>
import { ref, computed } from 'vue'
import { useMutation } from '@tanstack/vue-query'

const retryCount = ref(3)

// Options can be reactive
const mutationOptions = computed(() => ({
  mutationFn: createTodo,
  retry: retryCount.value,
}))

const { mutate } = useMutation(mutationOptions)
</script>

Reset Mutation State

<script setup>
import { useMutation } from '@tanstack/vue-query'

const { mutate, reset, isSuccess, error } = useMutation({
  mutationFn: createTodo,
})

const handleCreate = () => {
  // Reset previous state before new mutation
  reset()
  mutate({ title: 'New Todo' })
}
</script>

Build docs developers (and LLMs) love