advanced
Optimistic UI
Show expected mutation results before the server confirms them, with rollback and conflict handling.
Optimistic UI applies the expected mutation result before the server confirms it. Users perceive instant feedback; on failure, roll back to the previous cache snapshot and show an error.
useMutation({
mutationFn: updateTodo,
onMutate: async (patch) => {
await queryClient.cancelQueries({ queryKey: ['todos'] });
const previous = queryClient.getQueryData(['todos']);
queryClient.setQueryData(['todos'], (old) => mergeTodo(old, patch));
return { previous };
},
onError: (_err, _patch, ctx) => {
queryClient.setQueryData(['todos'], ctx.previous);
},
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
});
Requires idempotent-ish UX or conflict handling when server truth diverges. Pair with disabled double-submit, toast on rollback, and telemetry on failure rate.
The trade-off is instant perceived latency versus rollback UX complexity and conflict resolution when the server disagrees.
On interviews: cancel in-flight queries during optimistic update; why onSettled still refetches.
Common pitfalls: optimistic update without snapshot, irreversible actions (payment) optimistically, and UI that hides error state after rollback.
Checklist:
- Snapshot before optimistic write.
- cancelQueries to avoid overwrite races.
- Roll back on error with clear UX.
- Reconcile with server in onSettled.