Optimistic UI is the default in most modern web frameworks. The pattern is so common that engineers reach for it reflexively, even for actions where the pattern produces a worse experience than the slower alternative. The clearest example is destructive actions: deletes, account closures, cancellations, transfers, payments. For these, the right pattern is usually pessimistic UI, where the interface waits for the server to confirm before changing the visible state.
This is a defense of pessimistic UI for the cases where it actually wins, with a clear explanation of when not to use it.
What Pessimistic UI Looks Like
The user clicks "Delete." Instead of the item disappearing immediately, a spinner appears on the delete button, the item gets a subtle "deleting..." overlay, and the rest of the UI stays interactive but the deleted item does not change visually until the server confirms.
When the server confirms, the item disappears with a small transition. If the server rejects, the spinner goes away, the overlay clears, and an error message appears explaining why the delete did not go through.
The total elapsed time from click to confirmation is usually under 500 ms on a healthy connection. The user perceives a small delay, sees that the action took effect, and moves on. The interface stays honest the entire time.
Why Optimistic UI Fails on Destructive Actions
The promise of optimistic UI is that the user does not have to wait for the server. The cost is that the visible state and the server state can disagree for a moment, and the application has to reconcile when the response arrives.
For non-destructive actions (likes, bookmarks, edits with simple revert paths), the cost is small. The disagreement is brief, the reconciliation is mostly invisible, and the user experience is faster than pessimistic UI would have been.
For destructive actions, the cost gets larger.
The first cost is the rollback experience. If the user deleted a record optimistically and the server rejects, the record reappears. The user, who had moved on to the next task, is suddenly looking at the record they thought was gone. This is more disorienting than waiting half a second would have been.
The second cost is the time-to-confirmation. The user does not know whether the delete actually completed. They have to either trust the optimistic UI (and discover failures later) or wait for some kind of confirmation. The optimistic UI did not save them time; it just shifted the confirmation later.
The third cost is the multi-step interaction. Many destructive actions are confirmation-gated already. The user clicks "Delete," sees a confirmation modal, clicks "Yes, delete." The two-step interaction is the safety mechanism. Skipping the "actually wait for the delete to complete" step does not save the user time; the user already invested two clicks in being deliberate. Pessimistic UI honors that deliberation.
The fourth cost is the recovery path. If the delete fails server-side, recovery from a pessimistic UI is simple: the item never disappeared and the user retries. Recovery from an optimistic UI is harder: the item disappeared, the user moved on, and now it has to come back into view with an explanation. The explanation has to compete with whatever the user is now looking at.
When Pessimistic UI Earns Its Place
The actions where pessimistic UI is the right default include:
- Deletes of records the user cannot easily recreate
- Account or subscription cancellations
- Payment submissions
- Transfers of ownership (gifts, assignments, handoffs)
- Status changes that cascade to other systems (publishing, archiving, locking)
- Bulk operations that affect many records at once
The common thread is consequence. Each of these actions has a meaningful cost if it does not complete correctly, and the user benefits from seeing the confirmation before moving on.
Optimistic UI is right for actions where:
- The action is cosmetic or low-stakes (likes, bookmarks, sorting, filtering)
- The retry cost is near zero (a like that did not stick is easy to retry)
- The user expects to take many actions in rapid succession (drag-and-drop ordering, multi-select tagging)
- The network is reliable enough that the optimistic pattern almost always matches reality
The pattern matters more than the framework. React, Vue, Svelte, and the rest all support both patterns equally well. The choice is at the level of the individual action, not the application.
A Hybrid Pattern That Works for Most Actions
The honest answer for most actions is that they fit somewhere between fully optimistic and fully pessimistic. The hybrid pattern that works for most is:
The user takes the action. The UI immediately shows a "saving" or "deleting" indicator on the affected element. The element stays in place visually but is marked as "in flight." The rest of the UI is interactive and the user can do something else while the request is in flight.
When the server confirms, the in-flight indicator clears and the element updates (deletes, transitions, or shows the new value). If the server rejects, the indicator changes to an error state and the element snaps back to its pre-action state.
The user gets the responsiveness benefit (the rest of the UI is interactive) without the optimistic UI risk (the affected element does not lie about its state). The pattern is sometimes called "deferred optimistic" or "in-flight pessimistic." Both names point at the same thing.
For most production builds at https://137foundry.com, the hybrid pattern is the default for destructive actions, with fully optimistic reserved for cosmetic changes and fully pessimistic reserved for high-consequence actions like payments.
Why Modern Frameworks Make Both Patterns Easy
React Query, SWR, Apollo Client, and the rest all expose primitives for both patterns. The optimistic update primitives get the most documentation attention, but the pessimistic equivalents are usually one or two function calls away.
In React Query, the difference between optimistic and pessimistic is whether you call setQueryData before or after the mutate call resolves. Optimistic UI calls setQueryData immediately to apply the predicted state. Pessimistic UI calls setQueryData in the mutation's onSuccess handler, after the server confirms. The hybrid pattern uses neither, instead relying on a separate "in flight" piece of state to render the affected element with a loading indicator.
The longer guide on how to implement optimistic UI updates that roll back cleanly covers the state model that supports both patterns inside the same application. The architecture cost is small once the queue pattern is in place; the per-action decision becomes a matter of picking which rendering pattern to use, not which library primitive to call.
What This Means for Application Design
The deeper point is that "optimistic UI" is not a universally better pattern. It is a specific tool with specific tradeoffs, and the tradeoffs favor the user only when the action's consequence is low. For high-consequence actions, the pattern's tradeoffs work against the user.
A well-designed application uses both patterns. The choice is driven by the action's consequence, the network's reliability, and the user's likely next action. The choice is not a global setting on the data-fetching library; it is a property of each individual write endpoint.
For application teams, the practical implication is that the design phase needs to include a "rollback experience" review per write action. The reviewer asks three questions:
- What does the user see if the action succeeds?
- What does the user see if the action fails?
- What does the user see if the action is in flight and they take a related action?
The answers determine which pattern fits. Optimistic UI usually wins when the rollback experience is trivial. Pessimistic UI usually wins when the rollback experience would be jarring.
Where to Read More
For the full state model that supports both patterns, the longer guide on how to implement optimistic UI updates that roll back cleanly at 137Foundry covers the optimistic queue, the rollback patterns, and the reconciliation logic. The same state model works for pessimistic and hybrid patterns by adjusting when the queue entry's apply function fires (immediately for optimistic, on confirmation for pessimistic).
The Mozilla Developer Network guide on async patterns, the React Query documentation on mutation lifecycle, and the Nielsen Norman Group writeup on response time perception cover the human-factors side of the choice. The 100-millisecond, 1-second, and 10-second thresholds the Nielsen Norman piece describes are the right anchor for deciding whether pessimistic UI's added latency is noticeable.
The Short Version
Optimistic UI is not the right default for destructive actions. The rollback experience is too jarring, the time-to-confirmation is not actually faster, and the deliberation the user already invested in confirming the action makes the wait acceptable. Pessimistic UI honors that deliberation by showing the result of the action only after the server has confirmed.
The hybrid pattern (deferred optimistic, in-flight pessimistic) works for most actions in between. The fully optimistic pattern remains the right call for cosmetic, low-stakes, rapid-succession actions where the user benefits more from the responsiveness than they lose from the occasional flicker.
The choice is per-action, not per-application. A well-designed system uses all three patterns in the same product, picked based on the action's consequence rather than the framework's defaults.
Top comments (0)