React useOptimistic: Instant UI Updates Done Right
Learn how to use React's useOptimistic hook with Server Actions to show instant UI feedback while mutations complete in the background.
Tags
React useOptimistic: Instant UI Updates Done Right
TL;DR
The useOptimistic hook shows instant UI changes while a Server Action runs in the background, automatically reverting if the action fails.
The Problem
When a user submits a form or toggles a like button, the UI traditionally waits for the server to respond before updating. This delay makes the app feel sluggish, especially on slow connections. You could manually manage pending states, but that means tracking optimistic values, error rollbacks, and race conditions yourself.
The Solution
Use useOptimistic to immediately show the expected result:
"use client";
import { useOptimistic } from "react";
import { toggleLike } from "@/app/actions";
type Message = {
id: string;
text: string;
likes: number;
likedByUser: boolean;
};
export function MessageCard({ message }: { message: Message }) {
const [optimisticMessage, setOptimisticMessage] = useOptimistic(
message,
(current, newLikedState: boolean) => ({
...current,
likedByUser: newLikedState,
likes: newLikedState ? current.likes + 1 : current.likes - 1,
})
);
async function handleToggleLike() {
const newState = !optimisticMessage.likedByUser;
setOptimisticMessage(newState); // instant UI update
await toggleLike(message.id); // server action runs in background
}
return (
<div>
<p>{optimisticMessage.text}</p>
<button onClick={handleToggleLike}>
{optimisticMessage.likedByUser ? "Unlike" : "Like"} ({optimisticMessage.likes})
</button>
</div>
);
}The like count updates immediately on click. If the Server Action fails, React automatically reverts to the real server state on the next render.
Why This Works
useOptimistic takes two arguments: the actual state from the server and a reducer function that computes the optimistic version. When you call the setter, React immediately renders with the optimistic value. Under the hood, React tracks that a transition is pending. Once the Server Action completes (or the parent re-renders with fresh server data), React replaces the optimistic state with the real state. If the action throws, the optimistic value is discarded and the UI snaps back to reality -- no manual rollback logic required.
Collaboration
Need help with a project?
Let's Build It
I help startups and established companies design, build, and scale world-class digital products. From deep technical architecture to pixel-perfect UI — let's bring your vision to life.
Related Articles
TypeScript Utility Types You Should Know
Five essential built-in generic utility types in TypeScript that will save you hundreds of lines of code.
Generate Dynamic OG Images in Next.js
Generate dynamic Open Graph images in Next.js using the ImageResponse API with custom fonts, gradients, and data-driven content for social sharing.
GitHub Actions Reusable Workflows: Stop Repeating Yourself
Create reusable GitHub Actions workflows with inputs, secrets, and outputs to eliminate YAML duplication across repositories and teams efficiently.