useOptimistic is a React hook that shows a temporary value while a Server Action is in flight, then snaps back to the real value when the action settles. It is how Next.js apps make likes, follows, and list changes feel instant instead of waiting on the server. It does not fetch anything itself; it only changes what renders while your action runs.
Here is a like button in a Client Component file such as app/like-button.tsx.
'use client'
import { useOptimistic } from 'react'
import { toggleLike } from './actions'
export function LikeButton({ liked }: { liked: boolean }) {
const [optimisticLiked, setOptimisticLiked] = useOptimistic(liked)
async function likeAction() {
setOptimisticLiked(!optimisticLiked)
await toggleLike(!optimisticLiked)
}
return <form action={likeAction}><button>{optimisticLiked ? 'Unlike' : 'Like'}</button></form>
}The moment the visitor clicks, the button label flips to Unlike, even though the server has not answered yet. When toggleLike resolves, the parent updates the real liked prop and the optimistic value converges on it. The component needs use client because the hook runs in the browser.
How the temporary state works
The optimistic value only exists while a transition is pending. Before the click it equals liked, during the action it equals the value you set, and after the action it equals liked again. The optimistic value and the real value converge in a single render when the transition completes, so there is no extra render to clear it.
If the action fails, the transition still ends, and React renders the real liked value. Because the parent only updates liked on success, a failure makes the button silently flip back to Like. That rollback is automatic, but you should still catch the error to show a message.
The setter runs inside a transition
The setter from useOptimistic must be called inside a transition or an action prop. A form action prop already runs inside a transition, which is why likeAction can call setOptimisticLiked directly with no extra wrapping.
Calling the setter outside an action triggers a React warning, and the optimistic value appears only briefly before snapping back. The form's action prop is what keeps the temporary value alive for the whole request.
For a plain click handler that is not a form, wrap the setter and the action call in startTransition. See calling Server Actions from event handlers and useTransition for that pattern.
Reducer for lists and multiple values
useOptimistic takes an optional second argument, a reducer. Without one, the setter takes the next value directly, which is all a toggle needs. With one, the setter takes a description of the change and the reducer decides how to apply it.
| Second argument | Setter receives | Use it when |
|---|---|---|
| Omitted | The next value | The change is a toggle or an increment |
| A reducer | The change to apply | You add or remove items from a list |
The reducer receives the current list and the change, and returns the next list. That matters when the base list changes while your action is pending, such as another visitor adding a row at the same time.
A comments list, for example, can optimistically append the new comment with a pending flag, then swap it for the saved record when the server responds.
The reducer runs against the current state each time, so the append lands on top of whatever the latest list holds. That is what you want when the base data can change underneath a pending action.
See useActionState for pending state and server errors when you also want the server's return value and error handling, and how to mutate data with a Server Action for the action that actually writes the change.
When the action fails
If the Server Action throws, React ends the transition and renders the real value again. The optimistic change disappears, so a failed delete brings the row back and a failed like unlikes the post.
That rollback is what makes optimistic UI safe. The worst case is a brief flicker back to the old state rather than a UI that claims a change the server never accepted.
It is not a complete failure story though. The rollback alone does not tell the visitor why anything failed, so catch the error around the action call and show a message next to the control they used.
Rune AI
Key Insights
- useOptimistic shows a temporary value while an action is in flight.
- Call the setter inside a transition or a form action prop.
- The optimistic value reverts to the real value when the action settles.
- Omit the reducer for simple toggles and pass one for lists.
- The real value must update on success, or the UI snaps back.
Frequently Asked Questions
Does useOptimistic work with Server Actions?
What happens when the action fails?
Do I always need startTransition?
Conclusion
useOptimistic renders a temporary value while a Server Action runs, then converges on the real value. Call the setter inside a transition, update the optimistic state before awaiting the action, and let React roll back automatically on failure.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.