Skip to content

useOptimistic: UI that doesn't wait for the server

A like that lights up instantly and a chat message that appears before the server stores it. And if something fails, React rolls it back for you.

Author
Jan Jarčík
Published
Reading time
2 min read
Updated
ShareXLinkedInBluesky

Optimistic UI is the trick that makes apps feel instant: you show the result of an action right away and let the server confirm it in the background. React 19 gives us a hook for it: useOptimistic.

Signature

TypeScript
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
  • state — the "true" value (from props or from the server),
  • updateFn(current, optimisticValue) — a pure function returning the optimistic version,
  • addOptimistic(value) — called inside an action or startTransition.

While the action is running, optimisticState is rendered. When it finishes, React goes back to the current state — whether the action succeeded (and state now contains the new item) or failed (and the optimistic change simply disappears).

Example: a chat

TSX
"use client";
import { useOptimistic } from "react";
 
type Message = { id: string; text: string; sending?: boolean };
 
export function Thread({ messages, send }: {
  messages: Message[];
  send: (text: string) => Promise<void>;
}) {
  const [optimistic, addMessage] = useOptimistic(
    messages,
    (current, text: string) => [
      ...current,
      { id: crypto.randomUUID(), text, sending: true },
    ],
  );
 
  async function action(formData: FormData) {
    const text = String(formData.get("text"));
    addMessage(text);
    await send(text); // server action, then data revalidation
  }
 
  return (
    <>
      <ul>
        {optimistic.map((m) => (
          <li key={m.id} style={{ opacity: m.sending ? 0.5 : 1 }}>{m.text}</li>
        ))}
      </ul>
      <form action={action}>
        <input name="text" />
      </form>
    </>
  );
}

Gotchas

  • Calling addOptimistic outside an action or transition logs a warning and the change is discarded immediately.
  • Optimistic state is temporary. The source of truth must arrive through state after the action — usually via data revalidation or a setState inside the transition.
  • Show the user that the item is still saving (opacity, a clock icon). Optimism doesn't mean lying.