What is an "optimistic update"?
What an "optimistic update" is
An optimistic update is an approach where the UI updates immediately, before the server has confirmed the data change.
That is, the application optimistically assumes the server operation will succeed, and updates the interface instantly, so the user does not feel a delay.
Example without an optimistic update:
async function handleLike(postId) {
await fetch(`/api/posts/${postId}/like`, { method: "POST" });
refetch(); // wait for the server's response, then update the UI
}Downsides:
- The "like" button is clicked → but the UI does not change until the response arrives.
- The user feels a 300-500 ms delay (or more on a bad connection).
- The app feels "sluggish".
Example with an optimistic update:
const queryClient = useQueryClient();
async function handleLike(postId) {
// 1. Update the UI locally right away
queryClient.setQueryData(['post', postId], (old) => ({
...old,
likes: old.likes + 1,
isLiked: true,
}));
try {
// 2. Send the request to the server
await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
} catch (err) {
// 3. If the server responds with an error, roll back the changes
queryClient.invalidateQueries(['post', postId]);
}
}The user sees the update instantly, even without a response from the server. Once the server confirms, everything is already in sync. If the server responds with an error, the data rolls back to the actual state.
A visual scenario
| Event | What the user sees | What happens in the code |
|---|---|---|
| Clicked the "like" button | the like becomes active | setQueryData() updates the cache immediately |
| The server receives the request | nothing changes in the UI | fetch() runs in the background |
| The server responds 200 OK | everything stays as is | the cache remains current |
| The server responds 500 | the like disappears | a rollback (invalidateQueries or rollback) |
Why this matters
1. Instant response
The user feels the app works "in real time", with no delay even on a slow network.
2. Smooth UX
The interface reacts right away, without loader "flickers". Optimistic updates are especially important in interactive actions:
- likes,
- commenting,
- voting,
- changing a name, status, or an item's position in a list.
3. Independence from the network
If the request fails, the local changes can be rolled back. The user does not lose context (they see that the action "got cancelled").
How it is implemented in practice
1. React Query
import { useMutation, useQueryClient } from '@tanstack/react-query';
function useLikePost() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (postId) => fetch(`/api/posts/${postId}/like`, { method: 'POST' }),
// 1. Optimistically update the cache
onMutate: async (postId) => {
await queryClient.cancelQueries(['post', postId]);
const prevPost = queryClient.getQueryData(['post', postId]);
queryClient.setQueryData(['post', postId], (old) => ({
...old,
likes: old.likes + 1,
isLiked: true,
}));
// 2. Return data for a rollback
return { prevPost };
},
// 3. On error, roll back
onError: (err, postId, context) => {
queryClient.setQueryData(['post', postId], context.prevPost);
},
// 4. On success, update or invalidate
onSettled: (postId) => {
queryClient.invalidateQueries(['post', postId]);
},
});
}onMutate → local update
onError → rollback
onSettled → final update/validation
2. SWR (Vercel)
import useSWR, { mutate } from 'swr';
function likePost(postId) {
// optimistically increase the like count
mutate(`/api/posts/${postId}`, async (data) => {
await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
return { ...data, likes: data.likes + 1, isLiked: true };
}, { optimisticData: { ...data, likes: data.likes + 1, isLiked: true }, rollbackOnError: true });
}3. Redux Toolkit Query
RTK Query also lets you temporarily update the cache locally:
const [updatePost] = useUpdatePostMutation();
updatePost(newData)
.unwrap()
.then(() => dispatch(api.util.invalidateTags(['Post'])))
.catch(() => revertLocalState());4. Typical use cases for optimistic updates
| Action | What we update locally |
|---|---|
| Like | Increase the like counter |
| Adding a comment | Add the comment to the list |
| Deleting an item | Remove it from the UI before the server responds |
| Editing a profile | Change the name/avatar locally |
| Reordering | Rearrange the items locally |
| Payment / order | Mark it "created" before server confirmation |
Summary
| What Optimistic Update does | Why it matters |
|---|---|
| Updates the UI before the server responds | instant response |
| Improves UX and perceived speed | no "delay" during interaction |
| Syncs up on success | the UI and server stay consistent |
| Rolls back on error | reliable on an unstable network |
Cuts down the number of loading states | the interface feels "alive" |
In short:
An optimistic update is a way to make the interface instantly responsive: the UI updates right away, optimistically assuming success, and if the server returns an error, React rolls back the changes.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.