Optimistic UI in React with useOptimistic
When a user likes a post or sends a message, waiting for the server before updating the screen feels sluggish. Optimistic UI shows the result immediately and corrects it if the request fails.
The hook
const [optimisticState, addOptimistic] = useOptimistic(actualState, (current, optimisticValue) => newState);
While an action is running, optimisticState includes your optimistic changes. When the action finishes, it falls back to actualState, which by then should hold the real server data.
A chat example
import { useOptimistic, useState } from 'react';
export default function Chat({ initialMessages }) {
const [messages, setMessages] = useState(initialMessages);
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(current, text) => [...current, { id: crypto.randomUUID(), text, sending: true }]
);
async function send(formData) {
const text = formData.get('text');
addOptimisticMessage(text); // shows instantly
const saved = await api.sendMessage(text); // real request
setMessages((m) => [...m, saved]); // replace with server data
}
return (
<>
<ul>
{optimisticMessages.map((m) => (
<li key={m.id} style={{ opacity: m.sending ? 0.6 : 1 }}>
{m.text} {m.sending && <small>(sending...)</small>}
</li>
))}
</ul>
<form action={send}>
<input name="text" />
<button>Send</button>
</form>
</>
);
}
What happens on failure?
If api.sendMessage throws, setMessages never runs. When the action ends, the optimistic message disappears because the real state never included it. Catch the error to show a message such as "Couldn't send, tap to retry".
Good candidates
- Likes, stars, bookmarks and toggles.
- Adding items to a list (todos, comments, chat).
- Reordering with drag and drop.
Avoid optimistic updates for things users must not misunderstand, such as payments or irreversible deletes.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.