What is useOptimistic
What useOptimistic() is
useOptimistic()is a React hook that lets you temporarily show an updated state in the UI, while an asynchronous action is running (for example, submitting a form, adding a comment, deleting an item, and so on).
In other words: it helps make the interface responsive and "alive", even if the request to the server takes 1-2 seconds.
Signature
const [optimisticState, addOptimistic] = useOptimistic(baseState, updateFn);| Argument | Description |
|---|---|
baseState | The current real state (for example, a list of comments, an array of tasks, etc.) |
updateFn | A function (state, newValue) => newState describing how to display the "optimistic" state |
| Returns | [optimisticState, addOptimistic] - the current state (accounting for the optimism) and a function to temporarily change it |
A real-life example
Suppose you're building a chat where messages are sent to the server. The user clicks "Send", but the response from the API arrives after a second. Without optimism, the UI freezes - the message appears only after the response.
With useOptimistic() you can show the new message immediately,
and then React automatically updates the UI once the server responds.
Example: adding a comment
import { useOptimistic, useActionState } from 'react';
async function postComment(prevState, formData) {
const comment = formData.get('comment');
await api.addComment(comment);
return { success: true };
}
export default function Comments({ comments }) {
const [state, formAction] = useActionState(postComment, null);
const [optimisticComments, addOptimistic] = useOptimistic(
comments,
(currentComments, newComment) => [
...currentComments,
{ id: Math.random(), text: newComment, pending: true },
]
);
async function handleSubmit(formData) {
const comment = formData.get('comment');
addOptimistic(comment); // instantly show the new comment
await formAction(formData); // wait for the server
}
return (
<form action={handleSubmit}>
<ul>
{optimisticComments.map(c => (
<li key={c.id}>
{c.text} {c.pending && '(sending...)'}
</li>
))}
</ul>
<input name="comment" />
<button type="submit">Send</button>
</form>
);
}Here:
useOptimisticimmediately creates a "virtual" list of comments with the new message;- When the server responds, React replaces the optimistic state with the real one;
- The user immediately sees their comment appear.
Other examples
Deleting an item
const [optimisticTodos, removeOptimistic] = useOptimistic(
todos,
(current, id) => current.filter(t => t.id !== id)
);
async function handleDelete(id) {
removeOptimistic(id); // instantly hide it
await api.deleteTodo(id); // the server confirms afterward
}Likes
const [optimisticLikes, toggleOptimistic] = useOptimistic(
likes,
(current, delta) => current + delta
);
async function handleLike() {
toggleOptimistic(1);
await api.likePost();
}How this works under the hood
- You keep the "real" state (for example,
commentsorlikes). - React builds an "optimistic" state on top of it, which is applied instantly.
- When the Action finishes (or the data updates from the server), React discards the optimistic state and syncs with the real one.
- If the operation ends in an error, React automatically "rolls back" the UI to the previous state.
The difference from a manual approach (useState)
Without useOptimistic, you would have to write:
setComments([...comments, newComment]); // manually
try {
await api.addComment(newComment);
} catch {
setComments(oldComments); // rollback
}Problems:
- you need to keep the old state around for the rollback,
- it's hard to sync with other updates,
- it can conflict with concurrent rendering.
With useOptimistic:
- React itself tracks the "virtual" changes,
- it automatically syncs and rolls back the state,
- it works safely under concurrent rendering.
When to use useOptimistic
It fits for:
- Interfaces with response delays (forms, comments, likes, carts);
- Lists and tables where items are added/removed;
- UX optimization - the user sees the action "instantly".
It should not be used:
- For data where an error must block the action (for example, payments);
- For state that depends on complex server-side transactions;
- When the "optimistic" version could confuse the user.
Comparison with other React 19 hooks
| Hook | Purpose |
|---|---|
useActionState() | Manages the state of an asynchronous action (result, error, pending) |
useFormStatus() | Tracks the submission status of a specific form |
useOptimistic() | Temporarily updates the UI for instant feedback |
useTransition() | Makes updates "low priority", but not optimistic |
Combined example: useOptimistic + useActionState
async function addTask(prev, formData) {
const task = formData.get('task');
await api.addTask(task);
return { success: true };
}
export default function TaskList({ tasks }) {
const [state, formAction] = useActionState(addTask, null);
const [optimisticTasks, addOptimistic] = useOptimistic(
tasks,
(list, newTask) => [...list, { id: Math.random(), text: newTask, pending: true }]
);
async function handleSubmit(formData) {
const task = formData.get('task');
addOptimistic(task);
await formAction(formData);
}
return (
<form action={handleSubmit}>
<ul>
{optimisticTasks.map(t => (
<li key={t.id}>{t.text} {t.pending && '(saving...)'}</li>
))}
</ul>
<input name="task" />
<button type="submit">Add</button>
</form>
);
}The user sees the new task instantly, and React finalizes it once the server confirms.
Summary
useOptimistic()is a React hook for building an instant UI response to asynchronous changes, while the server processes the action.It is perfect for UX where the sense of "speed" matters, while React itself manages the rollback and data reconciliation.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.