Next.js Curriculum
HardServer Actions100% Free35 min read

Optimistic UI Updates with useOptimistic

The 30-Second Interview Answer

useOptimistic is a React 19 hook that renders predicted mutation outcomes immediately in the UI before server confirmation. If the server action succeeds, the optimistic state is replaced seamlessly by the fresh server state. If the action fails or rejects, React automatically rolls back the UI to the prior confirmed state without manual compensation code.

Learning Objectives

Implement zero-latency UI interactions using React 19 useOptimistic.

Design rollback handling when asynchronous Server Actions fail or throw errors.

Combine useOptimistic with useTransition and Server Actions for smooth UX.

Prevent race conditions when multiple optimistic updates occur rapidly.

Prerequisites & Background Knowledge
React 19 hooksServer ActionsOptimistic UI patterns

Why Interviewers Care

High-impact senior engineering topic. Evaluates candidate mastery of polished, perceived-performance UI architectures (e.g. social likes, chat messages, todo items).

The Challenge

Demonstrate how to implement an optimistic "Like" counter using React 19 useOptimistic and a Next.js Server Action. Ensure immediate UI feedback and seamless rollback on network failure.

Code Architecture & Implementation

1'use client';
2
3import { useOptimistic, useTransition } from 'react';
4import { toggleLikeAction } from '@/app/actions/likes';
5
6interface LikeButtonProps {
7 postId: string;
8 initialLikes: number;
9 initialLiked: boolean;
10}
11
12export function LikeButton({ postId, initialLikes, initialLiked }: LikeButtonProps) {
13 const [isPending, startTransition] = useTransition();
14
15 // useOptimistic takes confirmed state and an updater function
16 const [optimisticState, setOptimisticState] = useOptimistic(
17 { likes: initialLikes, isLiked: initialLiked },
18 (state, update: { isLiked: boolean }) => ({
19 likes: update.isLiked ? state.likes + 1 : state.likes - 1,
20 isLiked: update.isLiked,
21 })
22 );
23
24 const handleToggle = () => {
25 const nextLiked = !optimisticState.isLiked;
26
27 startTransition(async () => {
28 // 1. Immediately apply optimistic UI change
29 setOptimisticState({ isLiked: nextLiked });
30
31 try {
32 // 2. Dispatch Server Action
33 await toggleLikeAction(postId, nextLiked);
34 } catch (error) {
35 // 3. If action throws, React automatically rolls back optimisticState
36 // to match confirmed props!
37 console.error('Failed to toggle like:', error);
38 }
39 });
40 };
41
42 return (
43 <button
44 onClick={handleToggle}
45 disabled={isPending}
46 className={`flex items-center gap-2 px-3 py-1 rounded border ${
47 optimisticState.isLiked ? 'bg-red-50 text-red-600' : 'bg-gray-50'
48 }`}
49 >
50 <span>{optimisticState.isLiked ? '❤️' : '🤍'}</span>
51 <span>{optimisticState.likes}</span>
52 </button>
53 );
54}

Architectural Explanation

Prior to React 19, developers had to manually maintain duplicate state variables (isOptimistic, previousValue), handle try/catch blocks, and write tedious rollback logic. useOptimistic streamlines this: you supply the current authoritative state and a reducer-like updater. When setOptimisticState is called inside a transition, React updates the UI synchronously. Once the async server transition completes, React replaces the optimistic value with the fresh server result. If an error is caught and the transition terminates, React drops the optimistic branch and reverts to the original state.

Common Mistakes & Anti-Patterns

Calling setOptimisticState outside of `startTransition` or a form action, resulting in React console warnings.

Mutating state objects directly inside the optimistic updater instead of returning a new state object.

Not revalidating server cache (`revalidatePath`), causing the UI to revert to stale data once the transition finishes.

Interview Follow-Up Traps

The interviewer asks: "What happens if the user clicks 5 times rapidly?" In React 19, transitions are queued; each optimistic update layers on top of the current state, and the server processes them in sequence.

Frequently Asked Questions

Does useOptimistic work inside Server Components?

No. Optimistic rendering represents interactive browser state and must be in a Client Component.

Yes! They integrate cleanly: useActionState manages confirmed action status while useOptimistic provides immediate visual feedback.

The optimistic state remains visible to the user throughout the 5 seconds until the promise resolves or rejects.