A Primer on Optimistic UI

Dan Imhoff
Published

There are two competing theories in UI development.

Pending UI, where a pending state is presented to the user until their action is committed (usually to a remote database), has been the dominant implementation for years. Loading spinners, progress bars, or even a lack of an indicator altogether have all been accepted practice, where the user is forced to wait before they can move on to another task.

Expectations are changing.

Optimistic UI has quickly been gaining traction among modern software engineering teams. And for good reason. Optimistic UI gives the perception of instant latency—a tremendous competitive advantage.

The practice itself isn’t new. Messaging apps have been optimistic for years. Having only a single text field in which to type, these apps move messages into the feed as soon as the user taps "send"—just in case your friend wants to blow up your phone with a string of furiously typed stream-of-consciousness messages.

Optimistic Mutations

In Optimistic UI, the results of mutating data are simulated and the UI is updated before receiving a response from the server. Once the response is received, the simulated result is replaced with the actual result from the server, thus ensuring data consistency while also making the UI respond to user actions instantaneously.

For example, let’s say a user updates their name in their profile.

  1. User fills in their new name into a text field and clicks submit.
  2. An HTTP request is sent to update the name on the server. At the same time, we update their name everywhere in the app with the submitted value and clear the text field.
  3. When the HTTP request finishes, we read the accepted value returned by the server and use it to update their name everywhere in the app once again. If the HTTP request fails, we must revert the change (replacing their name everywhere in the app again with the old value).

Don’t Make Me Wait

The purpose of optimistic mutations is so that users don’t have to wait for feedback before moving onto their next task. When we build UIs that instantly apply changes that users make and synchronize them in the background, we instill confidence and optimism in our users. We show them that we care about their experience and their time. We enable power users to fly through the app totally unrestrained.

On the other hand, building UIs that have noticeable lag for even the most basic operations can have quite the negative effect on our UX. Take GitHub reactions, for example:

Applying a reaction to an issue comment has a completely predictable outcome and thus is a perfect candidate for an optimistic mutation. Instead, users click on the emoji and are left waiting for the network request to finish before it applies the update.

(This particular example has no loading indicator either, so users are left wondering if anything is happening at all!)

Compare this experience with applying reactions on OpenPhone:

The emoji reaction is appended to the comment instantly, and the user can move on to bigger and better things.

The Optimism Mindset

Optimistic mutations are just the start. Not only can Optimistic UI be applied to writes, it can also be applied to reads.

As data is fetched, it can be cached locally such that it needn’t be re-fetched every time it needs to be read. Libraries such as TanStack Query and Apollo Client have auto-caching built in, but features such as prefetching take this further by loading data long before it needs to be read.

Thus, optimism can be applied to navigation, as well. By aggressively prefetching data (e.g. using loaders or a similar concept), we can dramatically lower the navigation time between views. If our app uses code splitting, we can prefetch the bundles in our app. We can use service workers to intercept asset requests and improve subsequent initial loads.

Implementing effective Optimistic UIs is a full-stack endeavor. Definitely not for the faint of heart. It involves challenging problems such as caching, real-time, data sync/dealing with conflicts, and poor (or no) network connectivity.

But, with the right mindset—and a little optimism—we can unlock this new tier of user experience and performance in the web apps we build and—I promise you—users will notice. ✌️

Optimistic UI in the Wild

  • Perhaps the gold standard for modern, optimistic web applications is Linear. Co-founder Tuomas Artman shared a presentation recently about the in-house sync engine that powers the impressive application and challenges involved with scaling it over the years.
  • The real-time, data-intensive web app for OpenPhone (which I’ve worked on for 2+ years) is without a doubt a testament to Optimistic UI.
  • Projects such as Remix and Apollo GraphQL offer strategies for implementing optimistic mutations in their docs.
  • The ambitious Replicache project aims to make real-time sync seamless for any web application.