React is always improving and evolving with new ways to approach things and also new hooks, one of which that I have really liked using is the useOptimistic hook!
I struggled in the beginning with why/when I would find the best opportunities to use it, Tiktok answered that:
A couple weeks ago When I was watching a video on TikTok, I liked it and INSTANTLY saw feedback that a heart was added to the post. An ad popped up at the end of the video and I immediately scrolled away. Went BACK to the video but saw the heart was gone.
That's when it hit me:
When I initially liked the post it gave me the feedback that the heart on the video registered but swiping away so quickly with the ad blocking things, it may not have registered the like.
Now this might not be TikToks issue but this made me realize HOW I could use it!
How does the hook work?
"useOptimistic is a React Hook that lets you optimistically update the UI." - React Docs
It lets you perform an action, and OPTIMISITICALLY, it assumes everything will work. It performs the action with the UI, so the user gets an immediate response.
If you want to see the code, I put it on stackblitz so it is easy to follow.
I put all of the code in the App.tsx file.
https://stackblitz.com/edit/vitejs-vite-tt72lmea?file=src%2FApp.tsx
In this video you can see how this app is working. The first two posts that get likes show it immediately even though there is a delay! This is because of the useOptimistic hook!
Notice how the bottom two posts don't update immediately—that's because they aren't using useOptimistic, so they wait for the server before updating.
import { useOptimistic, startTransition } from "react";
Need to import the optimistic hook and startTransition api.
const [optimisticPosts, addOptimisticPost] = useOptimistic(
posts,
This part sets up an "optimistic state", which means it updates the UI immediately before waiting for the real data from the server.
// Apply the optimistic update immediately
startTransition(() => {
addOptimisticPost(postId);
});
startTransition API tells React: “Hey, update the UI immediately, but don't make this the highest priority API call, perform the others first since the user already has the UI feedback and then we can process this API call as the lowest priority." This prevents the UI from blocking OTHER important updates and ensures a smooth experience.
NOW, if you wanted to, You COULD potentially add some additional features that will trigger in case something fails. This way, we do not display the wrong state, like removing the "like" if the API fails.

(The gif isn't easy to see so if you want to see the video of it you can find it in this twitter thread here https://x.com/DThompsonDev/status/1884821001671749971)
Honestly, this was fun to make and experiment with!
Let me know if this helped you understand useOptimistic hooks and let me know what I should do a technical breakdown of next!
Follow me on all platforms at DThompsonDev
Top comments (12)
You're not actually demonstrating anything that the hook does here. You'll see if you comment out your transition with the optimistic update nothing changes, the state still updates immediately. You could actually remove the hook entirely and state would still update optimistically the way you have written your code.
startTransition is not the hook. All startTransition API does is mark it as nonurgent allowing other calls to happen first. That is why even when it is commented out the optimistic hook is still working. That does not change the useOptimistic hook, it just changes the priority of the API calls and not using it is losing a big advantage this hook has to speed up the overall experience for the user on the page as a whole.
Since you use the useOptimistic hook, you can deprioritize the API call, allowing others to go first since the user is getting the UI feedback right away. This allows other calls to go first increasing their priority. This would not change the feedback of the UI since that is in the useOptimistic hook, NOT the API call. Hope that explains this in a clear way.
I wasn't concerned with the transition. I think you're missing that the update function is in that block. When you comment it out, there's no call to the update function. At that point you're not using the useOptimistic hook anymore, but the UI is still optimistic.
Plus, as I said, if you remove the hook entirely, the behaviour of your top two optimistic posts is the same. Try replacing
optimisticPosts
withposts
in the return of your mainApp
component and you'll see there's no difference.Ah I understand what you are trying to point out but you missed something. Let me explain.
In your example you said if you swap optimisticPosts with posts, it will still render the same way. Taking it a step further, if you swap posts with optimisticPosts it will still show a delay.
Why is that?
Well, if we look further in the line to the function of renderPost() we have 2 arguements. post and a boolean value. The boolean is what we are looking for. The boolean is for isOptimistic.
The boolean is what decides is we use handleLike which uses the useOptimistic hook or if we use handleRegularLike which does not use the hook.
I named optimisticPosts just for the demo so we could show the separation of the two so it would be easier to follow. It is an array method that is mapping over the array of posts. The Boolean in renderPost is what decides if we use the hook or not.
Let me know if that clears this up for you.
Yes, and my point is that your handleLike function updates optimistically without the hook. If you remove all references to this hook you still have optimistic UI. Try removing
addOptimisticPost(postId)
, it will still be optimistic. Go further and replaceoptimisticPosts
withposts
and it will still be optimistic. The hook isn't doing anything.AH! I now see what you were saying. The hook was working but I put the timeout in the wrong spot so it wasn't actually slowing down the API call. I just slowed down the console log statement so it was always just updating the UI instantly. I moved it and now it is working. If you comment out the startTransition it will not perform optimistically now.
It still doesn't work. Try two clicks on like. As in, like, followed by unlike before the first promise resolves. You will see the optimistic liked state, then the optimistic unliked state, and then it will revert to the liked state, which is wrong.
This is because there are two more mistakes. One is in the functional update after the transition. You're updating the liked state based on the old state value from the current render (currentPost), instead of using the most up to date state (provided in the arg to the functional update).
The other is that the mocked API call needs to be in the transition. The transition isn't working where it at the moment since the async work happens in between the two synchronous updates. By awaiting the API call outside of the transition, the optimistic update reverts before the API call has completed, so you see the incorrect state. If you fix those two bugs you'll have optimistic updates.
(Also as a further note, I think the correct code for this second issue is to wrap the optimistic update, the API call, AND the state update in a transition, and to add another nested startTransition within to wrap the second state update.)
I see what you are talking about. Never tried clicking that many times to see it. Just updated it and ran a bunch of clicks. Seems to be working accurately. Thanks for catching that and the comments! The feedback has been great. Going to work some of this into the blog now as well and add some more detail based on it. Really appreciate you doing that.
No worries and thanks for being receptive. Honestly, I checked a few other articles about this hook and almost all of them have similar mistakes. YouTube videos too.
I think it would also be useful to let readers know what advantage the hook provides over simply setting state before the API call and then setting state again to revert if the call fails. Like, why use a new hook to do something that we always did with useState. This is why I started reading articles about it but I couldn't find an explanation. I know now after tinkering with it.
The use with forms is also interesting since React 19 has added new capabilities to the
<form>
element.You said useOptimistic hook breakdown, and guess what there was nothing related to it in this article, may be instead of covering whole video ( which is really really unnecessary ), you could've just demonstrated what is this hook used for, why it was made etc etc.
And about that hook I'm not very optimistic about it, that's not my cup of tea
This is really good feedback. I am going to tweak this article a bit based on that to improve it. Thanks for giving it to me. You are right. I should add in some more details. I appreciate you giving it to me straight. Will work on this today. Sometimes I struggle with feeling like something is just too long and try to be direct but there should be more context on this.
Don't fear of long articles, if it's really helpful then people won't see length of articles and they won't even notice