Home
React
Suspense: useOptimistic()
Daniel Nguyen
Daniel Nguyen
July 31, 2025
1 min

Optimistic UI updates the interface before the server confirms a change, making interactions feel faster and more responsive.

1. The Problem: Waiting for the Server

Imagine a user checks a todo item. If the UI waits for the server response before marking it complete, the interaction feels slow.

Instead, update the UI immediately and synchronize the change with the server in the background.

Benefits:

  • Immediate feedback.
  • Better perceived performance.
  • More responsive interactions.

2. How useOptimistic Works

React’s useOptimistic lets you display a temporary optimistic state while an Action is pending.

const [optimisticState, setOptimisticState] =
useOptimistic(currentState);

The optimistic value can temporarily differ from the underlying state. When the Action finishes, React returns to the current value supplied by the parent unless that underlying state has also been updated.

3. Example: Optimistic Todo Toggle

import { useOptimistic } from "react";
function Todo({ todo, updateTodo }) {
const [optimisticComplete, setOptimisticComplete] =
useOptimistic(todo.isComplete);
return (
<form
action={async () => {
const nextComplete = !optimisticComplete;
setOptimisticComplete(nextComplete);
await updateTodo(todo.id, nextComplete);
}}
>
<button type="submit">
{optimisticComplete ? "✓" : "○"} {todo.text}
</button>
</form>
);
}

The button provides immediate visual feedback, while the form Action synchronizes the change with the server.

When the Action finishes, the optimistic value stops being active. The parent must update todo.isComplete with the confirmed server state so the UI remains consistent.

Important: If the request fails, the optimistic UI reverts when the Action ends, provided the underlying state was never changed to the optimistic value. You should still handle the error and inform the user.

4. Optimistic Form Submission

When creating a record, you can display a temporary version before the server returns the real one.

function PokemonForm({ createPokemon }) {
const [optimisticPokemon, setOptimisticPokemon] =
useOptimistic(null);
return (
<form
action={async (formData) => {
const name = String(formData.get("name") ?? "");
setOptimisticPokemon({ name, pending: true });
await createPokemon(formData);
}}
>
<input name="name" required placeholder="Pokémon name" />
<button type="submit">Create Pokémon</button>
{optimisticPokemon && (
<p>
{optimisticPokemon.name}
{optimisticPokemon.pending ? " (Saving...)" : ""}
</p>
)}
</form>
);
}

This is a simplified example. A production implementation should reconcile the optimistic item with the returned record and handle failures. If the parent owns the list of Pokémon, update that underlying list after the server confirms the creation.

5. Multi-Step Progress

For a workflow with multiple steps, such as creating, saving, and notifying, ordinary state is often clearer for progress messages.

function CreateForm() {
const [status, setStatus] = useState("Create");
async function handleCreate(formData) {
try {
setStatus("Creating...");
const pokemon = await createPokemon(formData);
setStatus("Saving...");
await savePokemon(pokemon);
setStatus("Finalizing...");
await notifyFleet(pokemon);
setStatus("Complete");
} catch {
setStatus("Failed. Please try again.");
}
}
return (
<form action={handleCreate}>
<input name="name" required />
<button type="submit">{status}</button>
</form>
);
}

Here, useState is appropriate because the status represents actual workflow progress, not merely a temporary optimistic version of the data.

6. When Should You Use useOptimistic?

Use it for actions where immediate feedback is valuable, such as liking a post, toggling a todo, adding a comment, or creating a list item.


Tags

#ReactSuspense

Share

Daniel Nguyen

Daniel Nguyen

Frontend Developer

Frontend developer specializing in React, Next.js, and JavaScript. Writing practical guides on modern web development at Dev98.

Expertise

React
Next.js
JavaScript
TypeScript
Python

Social Media

githublinkedinyoutubewebsite

Related Posts

React Suspense
Suspense: useFormStatus()
August 01, 2025
1 min
Dev98

Dev98

React · Next.js · Web development