Next.js 34: Optimistic Updates with useOptimistic
easy⏱ 5 mincoursenextjs
Instant UI, reconciled later
useOptimistic takes your current state and an updateFn(currentState, optimisticValue) that describes how to merge a new, unconfirmed value into it. Every render shows optimisticState, not the raw state — so as soon as you call addOptimistic(...), the new item shows up immediately, tagged however you like (e.g. { pending: true }).
import { useOptimistic, useState } from 'react';
const [optimisticComments, addOptimistic] = useOptimistic(
comments,
(current, newComment) => [...current, { ...newComment, pending: true }]
);
// inside the action, before awaiting the server:
addOptimistic({ id: crypto.randomUUID(), text });
Automatic rollback
The optimistic entry only exists while its action is pending. If the action throws (validation fails, the request errors), React simply drops the optimistic value on the next render of the real state — no manual cleanup array to maintain. You still choose what the real state becomes on failure (e.g. leave the comment out entirely).
Simulating in React 18
We keep two pieces of state instead of one hook: the confirmed comments (the 'real' state) and optimisticComments (what's rendered). Adding a comment updates optimisticComments immediately; the fake server call then either promotes it into comments (success) or the list reverts to comments unchanged (failure) — the same net effect as useOptimistic.
Optimistic comments
Keep comments (confirmed) and optimisticComments (shown) in separate useStates. On submit, append the new comment to optimisticComments with a pending: true flag right away, style pending items with reduced opacity and a '(posting…)' label, then after the fake delay either promote it into comments (if the text is valid) or roll optimisticComments back to comments (if it's empty — simulating a validation failure). Keep export default App.