Skip to main content

useMutationState

The useMutationState hook provides access to the state of one or more mutations. It can be used to track mutation status, data, errors, and more across your application.

Import

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

Signature

function useMutationState<TResult = MutationState>(
  options?: {
    filters?: MutationFilters
    select?: (mutation: Mutation) => TResult
  },
  queryClient?: QueryClient,
): Array<TResult>

Type Parameters

TResult
type
default:"MutationState"
The type of data to return for each mutation. Defaults to full MutationState.

Parameters

options
object
Configuration options
filters
MutationFilters
Filters to narrow down which mutations to include
mutationKey
MutationKey
Filter by mutation key
exact
boolean
If true, only match mutations with the exact mutation key
status
MutationStatus
Filter by mutation status: ‘idle’, ‘pending’, ‘success’, or ‘error’
predicate
(mutation: Mutation) => boolean
Custom predicate function to filter mutations
select
(mutation: Mutation) => TResult
Transform function to select specific data from each mutation. Defaults to returning the full mutation state.
queryClient
QueryClient
Optional QueryClient instance. If not provided, uses the context client.

Returns

mutations
Array<TResult>
An array of mutation results based on the select function. Defaults to an array of MutationState objects.

MutationState Type

interface MutationState<TData = unknown, TError = unknown, TVariables = unknown, TContext = unknown> {
  context: TContext | undefined
  data: TData | undefined
  error: TError | null
  failureCount: number
  failureReason: TError | null
  isPaused: boolean
  status: 'idle' | 'pending' | 'success' | 'error'
  variables: TVariables | undefined
  submittedAt: number
}

Examples

Get All Mutation States

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

function MutationStatus() {
  const mutations = useMutationState()

  return (
    <div>
      <p>Total mutations: {mutations.length}</p>
      {mutations.map((mutation, i) => (
        <div key={i}>
          Status: {mutation.status}
          {mutation.data && <p>Data: {JSON.stringify(mutation.data)}</p>}
          {mutation.error && <p>Error: {mutation.error.message}</p>}
        </div>
      ))}
    </div>
  )
}

Filter by Mutation Key

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

function PostMutations() {
  const postMutations = useMutationState({
    filters: { mutationKey: ['posts'] },
  })

  return (
    <div>
      {postMutations.map((mutation, i) => (
        <div key={i}>
          Post mutation {i + 1}: {mutation.status}
        </div>
      ))}
    </div>
  )
}

Select Specific Data

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

function PendingUploads() {
  // Get only the variables from pending upload mutations
  const pendingFiles = useMutationState({
    filters: {
      mutationKey: ['upload'],
      status: 'pending',
    },
    select: (mutation) => mutation.state.variables,
  })

  return (
    <div>
      <h3>Uploading {pendingFiles.length} files:</h3>
      <ul>
        {pendingFiles.map((file, i) => (
          <li key={i}>{file?.name}</li>
        ))}
      </ul>
    </div>
  )
}

Track Recent Errors

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

function ErrorList() {
  const errors = useMutationState({
    filters: { status: 'error' },
    select: (mutation) => ({
      error: mutation.state.error,
      variables: mutation.state.variables,
      submittedAt: mutation.state.submittedAt,
    }),
  })

  return (
    <div>
      <h3>Recent Errors</h3>
      {errors.map((item, i) => (
        <div key={i}>
          <p>Error: {item.error?.message}</p>
          <p>Time: {new Date(item.submittedAt).toLocaleString()}</p>
        </div>
      ))}
    </div>
  )
}

Show Success Messages

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

function SuccessToasts() {
  const recentSuccess = useMutationState({
    filters: {
      status: 'success',
      mutationKey: ['posts', 'create'],
    },
    select: (mutation) => mutation.state.data,
  })

  return (
    <div className="toasts">
      {recentSuccess.map((post, i) => (
        <div key={i} className="success-toast">
          Created post: {post?.title}
        </div>
      ))}
    </div>
  )
}

Upload Progress Tracker

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

interface UploadVariables {
  file: File
  progress?: number
}

function UploadProgress() {
  const uploads = useMutationState<UploadVariables>({
    filters: { mutationKey: ['upload'], status: 'pending' },
    select: (mutation) => mutation.state.variables as UploadVariables,
  })

  return (
    <div>
      {uploads.map((upload, i) => (
        <div key={i}>
          <p>{upload?.file.name}</p>
          <progress value={upload?.progress || 0} max={100} />
        </div>
      ))}
    </div>
  )
}

Custom Predicate

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

function RetryingMutations() {
  const retrying = useMutationState({
    filters: {
      predicate: (mutation) =>
        mutation.state.status === 'pending' &&
        mutation.state.failureCount > 0,
    },
  })

  return (
    <div>
      {retrying.length > 0 && (
        <p>Retrying {retrying.length} failed operations...</p>
      )}
    </div>
  )
}

Mutation History

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

function MutationHistory() {
  const history = useMutationState({
    select: (mutation) => ({
      id: mutation.mutationId,
      status: mutation.state.status,
      submittedAt: mutation.state.submittedAt,
      variables: mutation.state.variables,
    }),
  })

  return (
    <div>
      <h3>Mutation History</h3>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Status</th>
            <th>Time</th>
          </tr>
        </thead>
        <tbody>
          {history.map((m) => (
            <tr key={m.id}>
              <td>{m.id}</td>
              <td>{m.status}</td>
              <td>{new Date(m.submittedAt).toLocaleString()}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}

Aggregate Mutation Data

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

function MutationStats() {
  const mutations = useMutationState()

  const stats = mutations.reduce(
    (acc, m) => {
      acc[m.status]++
      return acc
    },
    { idle: 0, pending: 0, success: 0, error: 0 }
  )

  return (
    <div>
      <h3>Mutation Statistics</h3>
      <p>Pending: {stats.pending}</p>
      <p>Success: {stats.success}</p>
      <p>Error: {stats.error}</p>
    </div>
  )
}

Track Specific Mutation Instance

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

function Component() {
  const mutation = useMutation({
    mutationKey: ['posts', 'create'],
    mutationFn: createPost,
  })

  // Track all instances of this mutation across the app
  const allInstances = useMutationState({
    filters: { mutationKey: ['posts', 'create'] },
  })

  return (
    <div>
      <button onClick={() => mutation.mutate(newPost)}>Create Post</button>
      <p>Total create attempts: {allInstances.length}</p>
    </div>
  )
}

Notes

  • The hook subscribes to the mutation cache and updates when mutations change
  • Returns an array that can be empty if no mutations match the filters
  • Uses useSyncExternalStore for efficient subscriptions
  • The select function is useful for extracting only the data you need
  • Mutations are kept in memory even after they complete, allowing you to track history
  • Use filters to narrow down results and improve performance
  • The hook uses deep equality checking to prevent unnecessary re-renders
  • Each mutation has a unique mutationId that can be used for keying

Build docs developers (and LLMs) love