useOptimistic lets you show a different state while an async action is in progress. This creates a snappier user experience by assuming success.
Basic Usage
tsximport { useOptimistic } from 'react'; type Message = { id: string; text: string; sending?: boolean; }; function Chat({ messages }: { messages: Message[] }) { const [optimisticMessages, addOptimisticMessage] = useOptimistic( messages, (state, newMessage: string) => [ ...state, { id: crypto.randomUUID(), text: newMessage, sending: true } ] ); async function sendMessage(formData: FormData) { const text = formData.get('message') as string; addOptimisticMessage(text); // Show immediately await deliverMessage(text); // Actually send } return ( <> <ul> {optimisticMessages.map((msg) => ( <li key={msg.id} style={{ opacity: msg.sending ? 0.5 : 1 }}> {msg.text} {msg.sending && ' (sending...)'} </li> ))} </ul> <form action={sendMessage}> <input name="message" /> <button>Send</button> </form> </> ); }
How It Works
- Initial state:
optimisticStateequals the actual state - Action starts: Call
addOptimistic(value)to show optimistic state - During action:
optimisticStateshows the optimistic value - Action completes:
optimisticStatereverts to actual state (which should now include the change)
Like Button Example
tsxfunction LikeButton({ post }: { post: Post }) { const [optimisticLiked, setOptimisticLiked] = useOptimistic( post.isLiked, (_, newValue: boolean) => newValue ); async function toggleLike() { setOptimisticLiked(!optimisticLiked); await updateLikeStatus(post.id, !post.isLiked); } return ( <button onClick={toggleLike}> {optimisticLiked ? '❤️' : '🤍'} </button> ); }
Error Handling
If the action fails, the optimistic state automatically reverts:
tsxasync function sendMessage(formData: FormData) { const text = formData.get('message') as string; addOptimisticMessage(text); try { await deliverMessage(text); } catch (error) { // Optimistic state automatically reverts! // Show error to user toast.error('Failed to send message'); } }
Best Practices
- Use for actions likely to succeed
- Show visual indicators for pending state
- Handle errors gracefully
- Keep optimistic updates simple