TL;DR: Optimistic updates make your SaaS app feel instant by updating the UI before the server responds. TanStack Query's
onMutatecallback gives you a clean pattern: snapshot the current cache, apply the optimistic change, and roll back on error. This guide covers three patterns — single item updates, list mutations, and paginated lists — with full TypeScript examples.
Introduction
Users perceive speed as a feature. A 300ms server call feels sluggish. Optimistic updates eliminate this perception by showing the result immediately and reconciling with the server in the background.
TanStack Query offers first-class support for this pattern through the useMutation hook's lifecycle callbacks.
Basic Optimistic Update
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { updateProductFn } from '../server/products'
export function useOptimisticUpdate() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: updateProductFn,
onMutate: async (newProduct) => {
await queryClient.cancelQueries({ queryKey: ['product', newProduct.id] })
const previous = queryClient.getQueryData(['product', newProduct.id])
queryClient.setQueryData(['product', newProduct.id], newProduct)
return { previous }
},
onError: (err, newProduct, context) => {
queryClient.setQueryData(['product', newProduct.id], context?.previous)
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['products'] })
},
})
}
List Mutation with Optimistic Update
For list queries, you need to manipulate the cached array:
useMutation({
mutationFn: createProductFn,
onMutate: async (newProduct) => {
await queryClient.cancelQueries({ queryKey: ['products'] })
const previous = queryClient.getQueryData(['products'])
queryClient.setQueryData(['products'], (old: any) => ({
pages: old.pages.map((page: any, i: number) =>
i === 0 ? { ...page, items: [newProduct, ...page.items] } : page
),
pageParams: old.pageParams,
}))
return { previous }
},
onError: (err, _, context) => {
queryClient.setQueryData(['products'], context?.previous)
},
})
Rollback Decision Matrix
| Failure Type | UX Response | Technical Action |
|---|---|---|
| Network error | Revert + toast | onError rollback |
| Validation error | Show field error | Keep optimistic + mark invalid |
| Authorization error | Revert + redirect | onError + router redirect |
| Conflict (stale data) | Revert + refetch | onSettled invalidation |
Conclusion
Optimistic updates dramatically improve perceived performance. TanStack Query's mutation callbacks provide a clean, testable pattern for implementing them without the complexity of manual state management.
Pair optimistic updates with proper Cache Invalidation Strategies for a complete data synchronization solution. Learn how server functions integrate with TanStack Query in Server Functions Best Practices.