Blog Add optimistic UI updates without a state library

Add optimistic UI updates without a state library

2 min read

Add optimistic UI updates without a state library
TL;DR Optimistic UI updates the interface immediately, assuming the server will succeed, then confirms or rolls back when the response arrives. Keep the previous state, apply the change locally, send the request, and on failure restore the old state and tell the user. It makes an app feel instant.

The difference between an app that feels snappy and one that feels sluggish is often a single choice: do you wait for the server before updating the UI, or update immediately and reconcile? Optimistic updates take the second path, and they make an app feel instant. Here is the pattern, no state library required.

The idea

Most actions succeed. So instead of showing a spinner and waiting, you assume success: update the UI right away, send the request in the background, and only intervene if it turns out the action failed. The user gets an immediate response for the common case, and you handle the rare failure gracefully.

The pattern

Three steps, and one crucial safety net.

async function toggleLike(post) {
  const previous = post.liked;        // 1. remember the old state
  setLiked(!previous);                // 2. update the UI immediately
  try {
    await api.setLiked(post.id, !previous);  // 3. send the request
  } catch (e) {
    setLiked(previous);               // 4. roll back on failure
    showError("Could not update. Try again.");
  }
}

The whole trick is step one. Keep the previous state before you change anything, so you have something to restore if the request fails.

Handle the failure honestly

Optimism is not the same as ignoring failure. If the request fails, you must roll back to the saved state and tell the user, or you have shown them a lie: a like that did not register, a message that was not sent. The rollback plus a clear error is what keeps the UI trustworthy.

The user experience is: instant response almost always, and on the rare failure, a brief revert with an honest message. That is far better than a spinner on every action.

When to be careful

Optimism fits actions that usually succeed and are cheap to reverse: toggles, adds, edits. Be more cautious with actions that are expensive or confusing to undo, like a payment, where waiting for real confirmation is the honest choice. Match the pattern to how safe the assumption of success actually is.

Why it is worth it

Perceived speed is real speed to a user. An interface that responds the instant they act feels fast even when the network is slow, because the network work happens out of their way. The pattern costs a few lines, save the state, apply, send, roll back on error, and it changes how quick your whole app feels. That is a large return on a small amount of code.

FAQ

What is an optimistic UI update?

It is updating the interface right away, before the server confirms, on the assumption the action will succeed. The user sees an instant response instead of a spinner. When the server replies, you either leave the change in place or roll it back if it failed.

What happens if the server request fails?

You roll back. Before applying the optimistic change, you keep a copy of the previous state. If the request fails, you restore that state and let the user know, so they are never left believing something happened that did not.

Do I need a state library for this?

No. The pattern is just: save the old state, apply the new one locally, send the request, and on error restore the old state. Any component state works. Libraries can help with caching and complex cases, but the core idea needs no special tooling.