Optimistic Updates: Elevating Perceived Performance in Frontend Applications

Optimistic updates provide a powerful approach to enhancing the perceived performance of applications by anticipating successful operations and updating the user interface (UI) before server confirmation. This article delves into the efficient implementation of optimistic updates using React, React Query, and SWR, and discusses how they can significantly improve user experience (UX).

ShareShare

Optimistic Updates: Elevating Perceived Performance in Frontend Applications

In the realm of frontend development, the user's perception of an application's performance is often more impactful than the actual server response time. This perception, or perceived responsiveness, is what truly defines the user experience. One way to enhance this perceived responsiveness is through the use of optimistic updates.

Optimistic updates, as the name suggests, optimistically assume that a requested change in the application will be successful, and consequently, the UI is updated immediately to reflect this anticipated success. The network request associated with the change is sent concurrently, and if the server response contradicts the optimistic assumption, the UI is rolled back or patched accordingly.

Consider the ubiquitous 'Like' button as a classic example of optimistic updates in action. When a user clicks the 'Like' button on a post, the UI immediately reflects the like - the heart icon fills up instantly, and the 'Like' count increments. The server call associated with the 'Like' action runs in the background, almost invisible to the user.


The Performance Enhancement Potential of Optimistic Updates

While optimistic updates do not technically reduce the actual latency of server responses, they significantly improve the perceived latency from the user's perspective. By immediately reflecting the anticipated success of an action in the UI, optimistic updates eliminate the frustrating wait times often associated with server responses. This leads to a smoother, more responsive user experience that is not marred by annoying loading spinners or unresponsive interfaces, even under suboptimal network conditions.


Implementing Optimistic Updates: A Deep Dive

The manual implementation of optimistic updates in React involves the use of the useState hook to manage the state of the UI element in question. For instance, consider the case of a 'Like' button:

const [liked, setLiked] = useState(false);

const handleLike = () => {
  setLiked(true); // Optimistic UI Update

  fetch("/api/like", { method: "POST" })
    .then(res => {
      if (!res.ok) throw new Error();
    })
    .catch(() => {
      setLiked(false); // Rollback upon unsuccessful server response
    });
};

In this example, the useState hook is used to maintain the liked state of the post. The handleLike function, triggered when the 'Like' button is clicked, optimistically sets the liked state to true, immediately reflecting the like in the UI. The corresponding network request to the server is sent concurrently. If the server response indicates an error, the catch block is executed, rolling back the optimistic update by setting the liked state back to false.

While this manual implementation approach works, it necessitates the explicit handling of error cases and rollback logic.

To streamline this process, libraries like React Query provide more efficient solutions. React Query provides the useMutation hook, which can handle rollbacks, deduplicate queries, and perform background validations, significantly simplifying the implementation of optimistic updates.

Here is how the 'Like' button example could be implemented using React Query:

const mutation = useMutation(likePost, {
  onMutate: async (newData) => {
    await queryClient.cancelQueries("likes");
    const prevLikes = queryClient.getQueryData("likes");

    queryClient.setQueryData("likes", (old) => [...old, newData]);
    return { prevLikes };
  },
  onError: (err, newData, context) => {
    queryClient.setQueryData("likes", context.prevLikes);
  },
  onSettled: () => {
    queryClient.invalidateQueries("likes");
  },
});

In this implementation, the onMutate method is called before the mutation, allowing you to make optimistic updates to the UI and cancel any outgoing queries related to the data being mutated. If the mutation fails, the onError method reverts the UI to its state prior to the mutation. After the mutation either succeeds or fails, the onSettled method is used to invalidate and refetch any queries related to the mutation.

Another library that provides a more declarative approach to implementing optimistic updates is SWR. Here's how the previous example could be implemented using SWR:

mutate("/api/cart", async (cart) => {
  const updated = [...cart, newItem];
  await fetch("/api/cart", { method: "POST", body: JSON.stringify(newItem) });
  return updated;
}, { optimisticData: [...data, newItem], rollbackOnError: true });

In this code snippet, the mutate function is used to perform the optimistic update, with the optimisticData option providing the new state for the UI, and rollbackOnError ensuring that the UI is reverted to its previous state in the event of an error.


Real-World Applications of Optimistic Updates

Several popular applications leverage the power of optimistic updates to enhance their user experience:

  • Twitter: Actions such as liking, retweeting, and bookmarking tweets are reflected instantly in the UI, with the server sync happening in the background.
  • Notion: Changes made to documents in Notion are reflected instantly in the UI, even before the changes are sent to the server.
  • Figma: Changes made by multiple users are optimistically applied and then synced over a WebSocket.

Best Practices and Anti-Patterns

While optimistic updates can greatly enhance the user experience, they should be used judiciously. They are best suited for actions that are unlikely to fail, such as liking a post, saving a form, adding items to a cart, or toggling settings.

However, for actions with potentially severe consequences, such as making payments or deleting data, or in situations where the server state is highly volatile, optimistic updates can lead to inconsistencies between the UI and the server state. Therefore, in such cases, it is advisable to wait for the server response before updating the UI.

Another potential pitfall to avoid is not handling failures properly. If a server response contradicts an optimistic update and the UI is not rolled back correctly, it can lead to an inconsistent and confusing user experience. Similarly, using optimistic updates with destructive actions, such as deletion, or updating multiple pieces of state with no undo mechanism, can lead to irreversible mistakes.


Conclusion: Harnessing the Power of Optimistic Updates

Optimistic updates provide a powerful technique for improving the perceived performance of your application. By optimistically predicting the success of actions and reflecting these predictions immediately in the UI, they can make your application feel instantaneous.

However, while optimistic updates can significantly enhance the user experience, they also require careful handling of failure cases and rollback logic to ensure consistency between the UI and the server state. As such, they should be used judiciously and responsibly.

In the world of frontend development, delivering a fast, responsive user experience is often about perception. And optimistic updates, when used correctly, can go a long way in delivering the feeling of speed to your users.

Subscribe for Deep Dives

Get exclusive in-depth technical articles and insights directly to your inbox.

about

Ehsan Hosseini

Ehsan Hosseini

me [at] ehosseini [dot] info

Staff Software Engineer and Tech Lead with a track record of leading high-performing engineering teams and delivering scalable, end-to-end technology solutions. With experience across web, mobile, and backend systems, I help companies make smart architectural decisions, scale efficiently, and align technical strategy with business goals.

© 2025 Ehsan Hosseini. All rights reserved.