useOptimistic in React 19: Patterns and Pitfalls from Real Forms
The Mental Model
useOptimistic stores nothing durable. It's a view over your real state that is only live while a transition is pending.
const [optimistic, addOptimistic] = useOptimistic(
realState,
(current, action) => nextState
);
The first arg is the source of truth; the second is a pure reducer. The instant your action resolves and realState updates, React throws the optimistic value away and re-renders from truth. You never manually roll back.
Pattern 1: Append on Submit
const [optimisticComments, addOptimistic] = useOptimistic(
comments,
(state, text: string) => [...state, { id: crypto.randomUUID(), text, pending: true }]
);
The pending flag drives a dimmed style; the server action revalidates the list so it refreshes from source.
Pitfall 1: Fire It Inside a Transition
Calling addOptimistic from a plain onClick makes the value flash and vanish - there's no pending transition to keep it alive. Always trigger it from a form action or startTransition.
Pattern 2: Toggle with a Count
const [state, toggle] = useOptimistic(
{ liked, count },
(s) => ({ liked: !s.liked, count: s.count + (s.liked ? -1 : 1) })
);
Because the reducer receives the current optimistic value, rapid double-clicks compose correctly instead of fighting a stale closure.
Pitfall 2: Errors Need Their Own Signal
useOptimistic has no error channel. When the action throws, the optimistic value disappears - correct - but the user gets no explanation. Pair it with useActionState or a toast.
Pitfall 3: Unique Temporary Keys
Reusing id: 'temp' for every pending row makes React merge concurrent entries. Give each optimistic item a unique temp key and let the server row replace it on revalidation.
When I Skip It
For low-confidence writes - payments, destructive deletes, heavy validation - I keep an honest pending state. Showing success you can't guarantee erodes trust faster than a spinner.
Originally published on devya.dev. Also on eng-ahmed.com. Built by Devya Solutions.
Comments
No comments yet. Start the discussion.