Skip to main content

useMutation

The useMutation hook is used to create, update, or delete data. Unlike queries, mutations are typically used to modify server-side data.

Import

import { useMutation } from '@tanstack/react-query'

Signature

function useMutation<
  TData = unknown,
  TError = DefaultError,
  TVariables = void,
  TOnMutateResult = unknown,
>(
  options: UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
  queryClient?: QueryClient,
): UseMutationResult<TData, TError, TVariables, TOnMutateResult>

Type Parameters

TData
type
default:"unknown"
The type of data returned by the mutation function
TError
type
default:"DefaultError"
The type of error that can be thrown by the mutation function
TVariables
type
default:"void"
The type of variables object passed to the mutation function
TOnMutateResult
type
default:"unknown"
The type of the context returned from the onMutate callback

Parameters

options
UseMutationOptions
required
Configuration options for the mutation
mutationFn
(variables: TVariables) => Promise<TData>
required
The function that performs the mutation. Receives the variables passed to mutate.
mutationKey
unknown[]
Optional mutation key for identifying and tracking the mutation
onMutate
(variables: TVariables) => Promise<TOnMutateResult> | TOnMutateResult
This function will fire before the mutation function is fired and receives the same variables the mutation function receives. Useful for optimistic updates.
onSuccess
(data: TData, variables: TVariables, context: TOnMutateResult) => Promise<unknown> | unknown
This function will fire when the mutation is successful and receives the mutation’s result
onError
(error: TError, variables: TVariables, context: TOnMutateResult | undefined) => Promise<unknown> | unknown
This function will fire if the mutation encounters an error
onSettled
(data: TData | undefined, error: TError | null, variables: TVariables, context: TOnMutateResult | undefined) => Promise<unknown> | unknown
This function will fire when the mutation is either successful or encounters an error
retry
boolean | number | (failureCount: number, error: TError) => boolean
default:"false"
Number of retry attempts or function to determine if a mutation should be retried
retryDelay
number | (retryAttempt: number, error: TError) => number
Function that receives a retry attempt number and returns the delay to apply before the next attempt
networkMode
'online' | 'always' | 'offlineFirst'
default:"'online'"
Controls when the mutation function is allowed to execute
throwOnError
boolean | (error: TError) => boolean
default:"false"
Set this to true if you want errors to be thrown in the render phase
meta
Record<string, unknown>
Optional metadata that can be used by mutation observers or plugins
gcTime
number
default:"300000"
Time in milliseconds that unused/inactive cache data remains in memory
queryClient
QueryClient
Optional QueryClient instance to use. If not provided, the client from the nearest QueryClientProvider will be used.

Returns

mutate
(variables: TVariables, options?: MutateOptions) => void
Function to trigger the mutation. This is a synchronous function that doesn’t return a promise.Variables:
  • variables: The variables to pass to the mutation function
Options:
  • onSuccess: Override or extend the mutation’s onSuccess callback
  • onError: Override or extend the mutation’s onError callback
  • onSettled: Override or extend the mutation’s onSettled callback
mutateAsync
(variables: TVariables, options?: MutateOptions) => Promise<TData>
Async version of mutate that returns a promise. Useful when you need to await the result or handle it with promise chains.
data
TData | undefined
The data returned by the last successful mutation
error
TError | null
The error object for the mutation, if an error occurred
status
'idle' | 'pending' | 'error' | 'success'
The status of the mutation:
  • idle: The mutation is currently idle or in a fresh/reset state
  • pending: The mutation is currently running
  • error: The mutation encountered an error
  • success: The mutation was successful
isPending
boolean
Derived from status. Will be true if the mutation is currently executing
isSuccess
boolean
Derived from status. Will be true if the mutation is in success status
isError
boolean
Derived from status. Will be true if the mutation is in error status
isIdle
boolean
Derived from status. Will be true if the mutation is in idle status
reset
() => void
Function to clean the mutation internal state (e.g., data, error, status, etc.)
variables
TVariables | undefined
The variables passed to the mutation function
failureCount
number
The failure count for the mutation
failureReason
TError | null
The failure reason for the mutation retry
submittedAt
number
Timestamp of when the mutation was submitted

Examples

Basic Usage

import { useMutation } from '@tanstack/react-query'

function CreateTodo() {
  const mutation = useMutation({
    mutationFn: async (newTodo: { title: string }) => {
      const response = await fetch('/api/todos', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newTodo),
      })
      return response.json()
    },
  })

  return (
    <button
      onClick={() => {
        mutation.mutate({ title: 'New Todo' })
      }}
    >
      {mutation.isPending ? 'Creating...' : 'Create Todo'}
    </button>
  )
}

With Type Safety

interface Todo {
  id: number
  title: string
}

interface CreateTodoVariables {
  title: string
}

function CreateTodo() {
  const mutation = useMutation<Todo, Error, CreateTodoVariables>({
    mutationFn: async (newTodo) => {
      const response = await fetch('/api/todos', {
        method: 'POST',
        body: JSON.stringify(newTodo),
      })
      return response.json()
    },
  })

  // mutation.data is typed as Todo | undefined
  // mutation.error is typed as Error | null
  // mutation.mutate expects CreateTodoVariables
}

Optimistic Updates

import { useMutation, useQueryClient } from '@tanstack/react-query'

function UpdateTodo() {
  const queryClient = useQueryClient()

  const mutation = useMutation({
    mutationFn: updateTodo,
    onMutate: async (newTodo) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['todos'] })

      // Snapshot previous value
      const previousTodos = queryClient.getQueryData(['todos'])

      // Optimistically update to the new value
      queryClient.setQueryData(['todos'], (old) => [...old, newTodo])

      // Return context with snapshot
      return { previousTodos }
    },
    onError: (err, newTodo, context) => {
      // Rollback on error
      queryClient.setQueryData(['todos'], context.previousTodos)
    },
    onSettled: () => {
      // Refetch after error or success
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })
}

With Async/Await

function CreateTodo() {
  const mutation = useMutation({
    mutationFn: createTodo,
  })

  const handleSubmit = async () => {
    try {
      const newTodo = await mutation.mutateAsync({ title: 'New Todo' })
      console.log('Created todo:', newTodo)
    } catch (error) {
      console.error('Error creating todo:', error)
    }
  }

  return <button onClick={handleSubmit}>Create</button>
}

Invalidating Queries

import { useMutation, useQueryClient } from '@tanstack/react-query'

function CreateTodo() {
  const queryClient = useQueryClient()

  const mutation = useMutation({
    mutationFn: createTodo,
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })
}

Source

Implementation: useMutation.ts:19

Build docs developers (and LLMs) love