Optimistic UI
Optimistic UI means updating the interface immediately on user action - before the server confirms - then reconciling when the response arrives.
Why use it
- Feels instant to the user even on slow connections.
- Dramatically improves perceived performance.
- Used by Twitter/X, GitHub, Gmail for likes, reactions, toggles.
Manual implementation
tsx
function TodoItem({ todo }) {
const [optimisticDone, setOptimisticDone] = useState(todo.done);
const toggle = async () => {
setOptimisticDone(prev => !prev); // update UI immediately
try {
await api.updateTodo(todo.id, { done: !todo.done });
} catch {
setOptimisticDone(todo.done); // rollback on error
}
};
return <input type="checkbox" checked={optimisticDone} onChange={toggle} />;
}React Query - built-in optimistic updates
tsx
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (updatedTodo) => api.updateTodo(updatedTodo),
onMutate: async (updatedTodo) => {
await queryClient.cancelQueries({ queryKey: ['todos'] });
// save current state for rollback
const previous = queryClient.getQueryData(['todos']);
// optimistically update the cache
queryClient.setQueryData(['todos'], (old) =>
old.map(t => t.id === updatedTodo.id ? updatedTodo : t)
);
return { previous }; // context for onError
},
onError: (err, variables, context) => {
// rollback on failure
queryClient.setQueryData(['todos'], context.previous);
},
onSettled: () => {
// refetch to sync with server truth
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});React 19 - useOptimistic
tsx
const [optimisticTodos, addOptimistic] = useOptimistic(
todos,
(state, newTodo) => [...state, { ...newTodo, pending: true }]
);The key to good optimistic UI is a solid rollback strategy. Always save the previous state before mutating and restore it on error.
updated
did you know it?