DevAcademy
LearnReactuseOptimistic
AdvancedReact

useOptimistic

Learn how to show an expected result immediately, before an async action actually finishes, using useOptimistic.

Reading Time

14 min

Lesson

Lesson 28 of 42

The Problem: Waiting Feels Slow

When a user likes a post, sends a message, or checks off a to-do, waiting for the server to confirm before updating the UI makes the app feel sluggish — even if the request only takes a few hundred milliseconds.

What useOptimistic Does

useOptimistic(state, updateFn) lets you show an "optimistic" version of state immediately, assuming the async action will succeed. If the action fails, React automatically reverts back to the real state — no manual rollback code needed.

An Optimistic Like Button

import { useOptimistic } from "react";

function LikeButton({ likes, onLike }) {
  const [optimisticLikes, addOptimisticLike] = useOptimistic(
    likes,
    (currentLikes) => currentLikes + 1
  );

  async function handleLike() {
    addOptimisticLike(); // shown immediately
    await onLike();      // the real, slower update
  }

  return (
    <button onClick={handleLike}>
      ❤️ {optimisticLikes}
    </button>
  );
}

useOptimistic Arguments and Return Value

ItemPurpose
stateThe real, confirmed state (e.g. the actual like count from the server)
updateFn(currentState, optimisticValue)Computes the temporary optimistic state to show immediately
optimisticStateThe value to render — either the real state, or the optimistic one while pending
addOptimisticUpdateCall this to trigger the optimistic update

Automatic Rollback on Failure

If the underlying async action throws or rejects, React discards the optimistic value and re-renders using the real state passed to useOptimistic — the UI naturally "snaps back" to the accurate value without any explicit try/catch rollback logic.

A Chat Message That Reverts on Failure

function MessageThread({ messages, sendMessage }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (currentMessages, newMessage) => [
      ...currentMessages,
      { ...newMessage, sending: true },
    ]
  );

  async function handleSend(text) {
    const message = { id: Date.now(), text };
    addOptimisticMessage(message);
    await sendMessage(message); // if this throws, the optimistic message disappears
  }

  return (
    <ul>
      {optimisticMessages.map((m) => (
        <li key={m.id}>
          {m.text} {m.sending && "(sending...)"}
        </li>
      ))}
    </ul>
  );
}

Commonly Paired with Actions

useOptimistic is frequently used together with React 19 Actions (async functions passed to a form’s action prop) — the Action performs the real mutation, while useOptimistic keeps the UI feeling instant while it’s in flight.

Best Practice

Reach for useOptimistic for actions that succeed the vast majority of the time (likes, toggles, sending a message) — for actions with a meaningfully high failure rate, a visible loading state may communicate uncertainty more honestly than an optimistic update that often reverts.

Interview Questions

Quick Quiz

1. What does useOptimistic let you do?

2. What happens automatically if the underlying async action fails?

3. What kind of actions are the best fit for useOptimistic?