Recipe: Optimistic UI updates
Update the UI immediately, then reconcile with the server.
Pattern
// 1. Snapshot current state
const prev = items;
// 2. Apply change immediately
setItems(updated);
// 3. Send request
await fetch("/api/items", {
method: "POST",
body: JSON.stringify(newItem),
});
// 4. Rollback on failure
// (wrap in try/catch, restore prev)Key rules
- Always keep a rollback snapshot before mutating state.
- Use a pending flag to show spinners only on the changed row.
- Deduplicate by request ID to avoid double-inserts on retry.
- Revalidate with SWR or a background refetch after success.
When to avoid
Skip optimistic updates for destructive actions (deletes, transfers) where a false-positive confirmation is worse than a brief spinner. Always confirm server-side.