Optimistic UI is not a new thing (games developers work with the idea of client-side prediction for a long time), but the concept has been gaining a lot of attention lately.
The idea is simple. Based on a user action, an interface is updated, even though a request to the backend may still be pending. In the end, an optimistic UI is nothing more than a way to manage perceived performance and avoid loading states.
The workflow can be straightforward in React.
- Capture a user action (click a button, for example).
- Update the local state, which updates UI.
- Send the request.
- Get a response.
- If negative, rollback update the local state (step 2).
- If positive, do nothing or confirm step 2.
Eliminating small lags from the UI interaction seems to create a faster and much more responsive perceived user experience. However, there are some drawbacks involving optimistic UI.
First, optimistic UI is not a one-size-fits-all solution. One crucial point is that if there is a failure in the server (false positive), the UI update should be reverted graciously, and this is difficult to achieve if the action triggered by the user plays a role in the application's routing.
Plus, since there is the risk of false positives, optimistic UI doesn't seem a good idea for checking for an air flight or a cash transfer experience. Imagine saying to the user that this cash transfer was reverted.
On the other hand, for actions that are not so important (like a post or posting a quick message, for example), optimistic UI seems OK.
The second point is that optimistic UI works better with actions that generate boolean values (true or false, yes or no), things that, in case of false positive, can be easily reverted.
In my experience, the most critical point of the technique when something didn't go well is making it noticeable. Handling UI errors is very important. The interval between the action and the error message couldn't be more than two seconds.
I created a small app that shows how optimistic UI can be implemented. As soon as you answer one of the questions, the UI is updated, even though the request is still pending. The app can be seen here, and the code is here.
This post is part of a series about UX and UI engineering.
Photo by Brooke Cagle on Unsplash
Top comments (7)
Can be simplified if you send ajax request to server first, and then, based on the result, update the local state. Then you dont need to worry about local state rollbacks.
But then if the ajax request fails, you've lost user data.
Rather than rollbacks, I'd use an automatic retry-policy. Then, if that fails notify the user and let him do a manual retry. Should probably have a 'failed operations inbox' for those kinds of notifications.
But... But...
Great article. Appreciated 👏👏👏
Thank you for sharing. This is actually dated back to the terminal era, where local echo was invented because remote responses couldn't catch up with the typing speed of the user.
Definitely an interesting read (and topic).
Yeah I totally agree. From a user's perspective, I guess perceived performance is the only performance that really matters. As long as it can fail gracefully without side-effects. I'm implementing state machines on everything that does optimistic updates, I feel it helps to keep everything in check.
Great!