Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Optimistic UI is powerful precisely because it trades a guarantee for perceived speed — and that trade is only acceptable when the failure rate is low and the consequence of a rollback is minor. Applying it to financial transactions, irreversible deletes, or form submissions with complex validation creates a user experience that feels broken: the UI says 'done' and then takes it back, leaving the user unsure what actually happened. The decision heuristic is simple: if the mutation has a greater than 95% success rate and a revert is visually cheap and semantically reversible, go optimistic. Otherwise, use a spinner and block further interaction until you have a confirmed result.
Two side-by-side interactions: a pessimistic checkout button that shows a spinner until the server confirms, and an optimistic like button that toggles instantly. The contrast makes the appropriate use cases obvious.
Math.random() < 0.5 failure to CheckoutButton and observe how the missing rollback leaves the UI in a 'done' state even though the payment failed — note why this is dangerous.CheckoutButton to rollback (setStatus('idle')) on failure with an error message, then decide whether this changes your opinion on whether checkout should be optimistic.LikeButton pessimistically (disable the button until the fetch resolves) and time the difference in perceived responsiveness using performance.now().import { useState } from "react";
// Pessimistic: appropriate for checkout
function CheckoutButton() {
const [status, setStatus] = useState<"idle" | "pending" | "done">("idle");
async function handleCheckout() {
setStatus("pending");
await new Promise((r) => setTimeout(r, 1200)); // payment API
setStatus("done");
}
if (status === "done") return <p>Order confirmed!</p>;
return (
<button disabled={status === "pending"} onClick={handleCheckout}>
{status === "pending" ? "Processing..." : "Pay $49"}
</button>
);
}
// Optimistic: appropriate for social actions
function LikeButton() {
const [count, setCount] = useState(128);
const [liked, setLiked] = useState(false);
async function handleLike() {
const prev = { count, liked };
setLiked(!liked);
setCount((c) => (liked ? c - 1 : c + 1));
try {
await new Promise((r) => setTimeout(r, 600));
} catch {
setLiked(prev.liked);
setCount(prev.count);
}
}
return (
<button onClick={handleLike}>
{liked ? "❤️" : "🤍"} {count}
</button>
);
}