Optimistic UI

Optimistic UI


"I want to lie to the user", I told my colleagues.

Last Friday, I ran into a tricky UI problem, and I've been wanting to share the solution I came up with after brainstorming with my colleagues.

The Problem

I have two buttons: Like and Dislike. A request to the backend is made when the page loads, fetching the current status (whether the item is liked or disliked). If the status is "disliked", and the user clicks the Like button, the Dislike button should no longer be in the "active" state. Anytime either of these buttons is clicked, a PATCH request is made to update the status.

The Challenge

Here’s the issue: users won’t know if their Like or Dislike action has been successfully processed until the request completes and the page reloads. In the meantime, users might keep clicking the button, bombarding the backend with repeated requests. That’s frustrating.

So, I stood up from my seat and told Adeyanju Tomide and Chioma Ezezim , “How can I lie to the users?”


The Solution

That’s where Optimistic UI comes into play.

Optimistic UI is a technique that allows you to "fake" the update for the user interface, making it seem like the request was successful immediately, even though the request is still processing in the background.


But What If the Request Fails?

That led us to the next question: What happens if the request fails?

Here are some of the approaches I considered:

  1. Invalidate the Status with TanStack Query: Initially, I thought about invalidating the status when the request is made and just refetching it. The issue with this is that slow network requests could leave the user without any visual feedback. They wouldn't know if the action succeeded until the network response came in.
  2. Use Local Storage to Save State: Another thought was to save the user’s status in localStorage and use that for immediate feedback. However, localStorage isn’t ideal when you're updating multiple states at once. Plus, I didn’t want to risk inconsistencies in the UI if too many things were being updated at the same time.
  3. Local State with useState and TanStack Query: The approach I ended up using combined local state (via useState) with TanStack Query to manage both the UI and backend state. Local state is used to optimistically update the button's status on the UI when clicked, providing immediate feedback. If the request succeeds, we keep the updated state. If the request fails, the UI "bounces back" to the original state using local state rollback. I also added logic to prevent users from repeatedly clicking the button, ensuring that multiple requests aren't sent before the previous one completes.
  4. Optimistic Updates with TanStack Query: Another approach I later came across this evening, since I am using TanStack Query is using TanStack Query’s built-in optimistic update feature. With onMutate, onError, and onSettled, you can update the UI immediately, and if the request fails, rollback automatically. This method avoids manual useState management and keeps the UI and server data properly in sync.


The Outcome

Even though I initially used useState to manage local UI updates, moving toward TanStack Query’s Optimistic Update API is a cleaner and more scalable solution when working with server data, and I will be updating the codebase soon. Or when do you think I should stick to my current solution?


If you’ve run into similar UI problems, I’d love to hear how you approached them. Do you have any better suggestions for handling such cases?


#frontend #react #optimisticUI #userexperience #tanstackquery #webdevelopment



To view or add a comment, sign in

More articles by Samson Ameh

Others also viewed

Explore content categories