Skip to the solution
HardFrontend5 min read

How do you do optimistic updates in Next.js (useOptimistic, useActionState)?

960 views

The problem

Question presented to candidate: "A like button feels slow because the server takes a second and a half. How do you make it feel instant with useOptimistic, and what happens if the server rejects the like?"

What a strong answer should cover:

  • useOptimistic(state, updateFn) returns a value that shows an optimistic change while an action or transition is pending.
  • Call the optimistic setter inside a transition (a form action or startTransition) together with the real action.
  • When the action finishes, React discards the optimistic value and shows the real state from the server.
  • If the action fails or returns an unchanged state, the optimistic change disappears automatically; show an error from the action result.
  • useActionState holds the server result and a pending flag; the two hooks are usually used together.

Clarifying questions expected:

  • "Can the action fail, and how should that look?" β€” decides the error message and whether to retry.
  • "Is the change safe to show before it is confirmed?" β€” optimistic UI suits likes and toggles, not payments.

Code / implementation expected: Yes β€” a like button with useOptimistic and useActionState.

  • nextjs
  • useoptimistic
  • useactionstate
  • server-actions

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

5 min read Β· 8 sections

Read the code

A like button that updates instantly and rolls back on failure
"use client";
import { useActionState, useOptimistic, startTransition } from "react";
import { like } from "./actions"; // (prev, formData) => Promise<{ count: number; error?: string }>

export function LikeButton({ postId, initial }: { postId: string; initial: number }) {
  const [state, likeAction, pending] = useActionState(like, { count: initial });
  const [shown, addOptimistic] = useOptimistic(state.count, (c: number) => c + 1);

  function onLike() {
    const fd = new FormData();
    fd.set("postId", postId);
    startTransition(() => {
      addOptimistic(null);
      likeAction(fd);
    });
  }

  return (
    <div>
      <button onClick={onLike} disabled={pending}>Like ({shown})</button>
      {state.error && <p role="alert">{state.error}</p>}
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 104 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextHard

What are Server Actions in Next.js and how do they work?

Continue