Mutations with React Query
In this page:
What Mutations Are For
While useQuery handles reading data, useMutation handles writing it — creating, updating, or deleting something on the server. It's built around a mutationFn (the actual API call) and a mutate function you call with the data to send.
Note: Think of useQuery as GET requests and useMutation as POST/PUT/DELETE requests.
Warning: Mutations don't run automatically on render like queries can — you always trigger them explicitly, usually from an event handler.
Example: What Mutations Are For
// Run in your local React project (npm install required)
import { useMutation } from '@tanstack/react-query';
function AddUserForm() {
const mutation = useMutation({
mutationFn: newUser => fetch('/api/users', { method: 'POST', body: JSON.stringify(newUser) }),
});
return <button onClick={() => mutation.mutate({ name: 'Asha' })}>Add User</button>;
}
⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.
Refreshing Related Data After a Mutation
After successfully adding, updating, or deleting something, the cached query for the related list (like [users]) is now stale. Calling queryClient.invalidateQueries in the mutation's onSuccess callback tells React Query to refetch that query, keeping the UI in sync with the server.
Note: Invalidate the specific query key related to what changed, rather than invalidating everything, to avoid unnecessary refetches.
Warning: Skipping invalidation after a mutation means the UI keeps showing the old list, even though the server's data has already changed.
Example: Refreshing Related Data After a Mutation
// Run in your local React project (npm install required)
import { useMutation, useQueryClient } from '@tanstack/react-query';
function AddUserForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: newUser => fetch('/api/users', { method: 'POST', body: JSON.stringify(newUser) }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
});
return <button onClick={() => mutation.mutate({ name: 'Asha' })}>Add User</button>;
}
⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.
Showing Mutation Status in the UI
The object returned by useMutation includes isPending, isError, and isSuccess flags, letting the UI disable a button while the request is in flight, show an error message if it failed, or a confirmation if it succeeded — the same shape of feedback loading/error states need elsewhere.
Note: Disable the trigger button while isPending is true, to prevent duplicate submissions.
Warning: Ignoring isError means a failed mutation fails silently, with the user having no idea their action didn't actually go through.
Example: Showing Mutation Status in the UI
// Run in your local React project (npm install required)
import { useMutation } from '@tanstack/react-query';
function AddUserForm() {
const mutation = useMutation({
mutationFn: newUser => fetch('/api/users', { method: 'POST', body: JSON.stringify(newUser) }),
});
return (
<div>
<button onClick={() => mutation.mutate({ name: 'Asha' })} disabled={mutation.isPending}>
{mutation.isPending ? 'Adding...' : 'Add User'}
</button>
{mutation.isError && <p>Failed to add user</p>}
</div>
);
}
⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.
- Forgetting to call invalidateQueries after a successful mutation, leaving stale cached data displayed.
- Not handling the mutation's isPending/isError states, giving no feedback during a slow or failed write.
- Calling the mutate function directly with the wrong argument shape, not matching what mutationFn expects.
- useMutation manages create/update/delete operations, as opposed to useQuery's read operations.
- Calling mutate(variables) triggers the mutationFn with those variables.
- invalidateQueries tells React Query a related query's cached data is now stale and should be refetched.
- The mutation object exposes isPending, isError, and isSuccess for showing appropriate UI feedback.
Requires npm install @tanstack/react-query — not available via CDN in this sandbox.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: