React 19 is here and it comes with a new hook called useOptimistic
.
In this blog post, we'll take a deep dive into the useOptimistic
hook and learn how you can use it in your projects.
Note : The useOptimistic
hook is a new feature in React 19 Canary and is not available in earlier versions of React.
But, before diving into useOptimistic
, let's first understand what optimistic UI is.
What is Optimistic UI?
Optimistic UI is a design pattern where the UI is updated optimistically before the result of an operation is known. This means that the UI is updated immediately after an action is triggered, without waiting for the response from the server. This is the text book definition of optimistic UI, and can be difficult for some to grasp at first.
Let's take an example to understand this better.
Suppose, you are building an Chat Application where users can send messages to each other. Let's say we have to individuals A and B. When A sends a message to B, the message is first sent to the server and then the server sends the message to B. Supposedly, you are a bad dev (we all are T_T) and your backend is really slow and takes about 5 seconds to perform all tasks necessary before sending the message to B. Now, if you were to wait for the server to send the message to B and then update the UI, the user experience would be terrible.
This is where optimistic UI comes in.
With optimistic UI, you can update the UI immediately after the user sends the message, because you are optimistic that the message will be sent successfully.
You can see this in action in a lot of real world applications. For example, when you send a message on WhatsApp, the message is immediately shown in the chat window, even before the message is sent to the server. To further enhance the user experience, you can also see a timer icon below the message to indicate to the user that the message is being sent.
Implementation of Optimistic UI Pattern
Let's try implementing the optimistic UI pattern in a simple example. For this example, we'll build a like button that changes it's color when clicked.
Here's a simple Like Button created without the optimistic UI pattern:
As you can see, this implementation is very bad for the user experience. The user has to wait for the server to respond before they can see the change in the button color.
I'll provide the code for the Like Button component below:
'use client'
import { useCallback, useState } from "react";
export default function Like() {
const [liked, setLiked] = useState(false);
const [loading, setLoading] = useState(false);
const [status, setStatus] = useState("");
const styles = {
color: liked ? "gold" : "gray",
background: "transparent",
outline: "none",
border: "none",
fontSize: "2rem"
};
const mockRequest = useCallback(() => {
return new Promise((res, rej) => {
setStatus("Processing Request...")
setLoading(true);
setTimeout(() => {
res()
}, 5000);
});
}, []);
const handleClick = useCallback(() => {
mockRequest()
.then(() => {
console.log("Done...")
})
.catch((err) => {
console.log("Err : ", err);
setLiked((prev) => prev);
setLoading(false);
})
.finally(() => {
setLiked((prev) => !prev);
setLoading(false);
setStatus("");
});
}, [liked]);
return (
<div className="flex flex-col justify-center items-center gap-2">
<button type="button" onClick={handleClick} style={styles} disabled={loading} className="disabled:cursor-wait">
❤
</button>
<p className="text-white text-xs">{status}</p>
</div>
);
}
This is a pretty straight forward implementation of the Like Button component.
When the button is clicked, the handleClick()
function is called which simulates a request to the server using the mockRequest
function.
The mockRequest()
function returns a promise that resolves after 5 seconds.
Now, let's try improving this implementation. There are various ways we can go about this, but in this blog post, we'll focus on two ways:
- Using the native approach
- Using the
useOptimistic
hook
Using the Native Approach
The Native Approach is a bit better than the previous implementation, but it still has some issues, in the sense that it is not very clean and can be difficult to maintain as the codebase grows.
Here are few of the modification we had to make :
-
Initializing
optimistic
statesconst [optimistic, setOptimistic] = useState(false);
-
Updating the
handleClick
functionconst handleClick = useCallback(() => { setOptimistic(!liked); mockRequest() .then(() => { console.log("Done...") }) .catch((err) => { console.log("Err : ", err); setOptimistic(liked) setLoading(false); }) .finally(() => { setOptimistic(!liked); setLiked((prev) => !prev); setLoading(false); setStatus(""); }); }, [liked]);
-
Updating the
styles
objectconst styles = { color: optimistic ? "gold" : liked ? "gold" : "gray", background: "transparent", outline: "none", border: "none", fontSize: "2rem" };
And tadah 🎉, we have a better implementation of the Like Button component using the Native Approach.
Using the useOptimistic
Hook
Now, let's see how we can implement the Like Button component using the useOptimistic
hook.
-
Initializing
useOptimistic
states :const [optimisticLiked, setOptimisticLiked] = useOptimistic(false, (state, newValue) => newValue);
-
Updating the
handleClick
function :const handleClick = useCallback(() => { // Optimistically set the liked state setOptimisticLiked(!optimisticLiked); mockRequest() .then(() => { console.log("Request successful"); }) .catch((err) => { console.error("Request failed:", err); // Revert the optimistic update in case of an error setOptimisticLiked(optimisticLiked); }) .finally(() => { setStatus(""); }); }, [optimisticLiked]);
-
Updating the
styles
object :const styles = { color: optimisticLiked ? "gold" : "gray", background: "transparent", outline: "none", border: "none", fontSize: "2rem" };
And that's it! We have successfully implemented the Like Button component using the useOptimistic
hook.
You can learn more about the useOptimistic
hook in the official React documentation.
Conclusion
In this blog post, we learned about the optimistic UI pattern and how we can implement it in our
React applications using the useOptimistic
hook.
The useOptimistic
hook is a powerful tool that can help you improve the
user experience of your applications by updating the UI optimistically before the result of an
operation is known.
I hope you found this blog post helpful. If you have any questions or feedback, reach out to me on Twitter , LinkedIn or you can leave a mail