Optimistic Updates
Users expect instant feedback. Optimistic updates let your UI respond immediately while the agent processes in the background. This lesson covers how to implement optimistic patterns for agent interactions, handle rollbacks when things go wrong, manage pending states, and deal with concurrent requests gracefully.
Optimistic UI for Agent Responses
The simplest optimistic update is showing the user's message in the chat immediately, before the server confirms receipt. But optimistic patterns go deeper with AI agents: you can predict tool call outcomes, pre-render expected UI changes, and show placeholder content that transitions smoothly into the real response.
import { useAgent } from '@waymakerai/aicofounder-react';
import { useState, useCallback } from 'react';
function OptimisticChat() {
const [messages, setMessages] = useState<Message[]>([]);
const { send } = useAgent({
endpoint: '/api/agent/chat',
onComplete: (response) => {
// Replace the optimistic message with the real one
setMessages((prev) =>
prev.map((msg) =>
msg.id === response.requestId
? { ...msg, status: 'confirmed', agentReply: response.text }
: msg
)
);
},
onError: (error, requestId) => {
// Mark the optimistic message as failed
setMessages((prev) =>
prev.map((msg) =>
msg.id === requestId
? { ...msg, status: 'failed', error: error.message }
: msg
)
);
},
});
const handleSend = useCallback((text: string) => {
const requestId = crypto.randomUUID();
// Optimistically add the user message and a placeholder reply
setMessages((prev) => [
...prev,
{ id: requestId, role: 'user', content: text, status: 'pending' },
{ id: requestId + '-reply', role: 'assistant', content: '', status: 'streaming' },
]);
send(text, { requestId });
}, [send]);
return (
<div>
{messages.map((msg) => (
<MessageBubble key={msg.id} message={msg} />
))}
</div>
);
}Rollback Strategies
When an optimistic update fails, you need a clear rollback strategy. The three main approaches are: snapshot-based rollback (save the previous state and restore it), reverse-action rollback (apply an inverse operation), and reconciliation (merge the server state with the optimistic state).
For agent interactions, snapshot-based rollback works best. Before applying the optimistic update, save the current messages array. If the request fails, restore the snapshot and show an error toast. CoFounder's useAgent hook supports a rollbackOnError option that handles this automatically.
Pending State Management
When a message is in a pending state, the UI should communicate that clearly without being disruptive. Use subtle visual cues: a slightly reduced opacity, a small spinner, or a pulsing indicator. Avoid blocking the entire UI; the user should be able to scroll through history and even queue additional messages while one is pending.
function MessageBubble({ message }: { message: Message }) {
return (
<div className={`message ${message.status === 'pending' ? 'opacity-80' : ''}`}>
<div className="message-content">
{message.content}
{message.status === 'pending' && (
<span className="inline-block ml-2 animate-pulse text-xs text-muted">
Sending...
</span>
)}
{message.status === 'failed' && (
<div className="mt-2 text-sm text-red-400 flex items-center gap-2">
<span>Failed to send</span>
<button
onClick={() => retrySend(message.id)}
className="underline hover:text-red-300"
>
Retry
</button>
</div>
)}
</div>
</div>
);
}Concurrent Request Handling
Users often send multiple messages before the first response arrives. Your application needs to handle this gracefully. The key decisions are: should you queue messages, send them in parallel, or cancel the previous request? CoFounder supports all three strategies via the concurrency option.
For chat interfaces, queuing is usually correct: each message depends on the context of previous ones. For independent agent tasks (like generating multiple summaries), parallel execution is more efficient. For search-as-you-type scenarios, cancellation of the previous request makes the most sense.
Optimistic Tool Call Results
Advanced optimistic patterns can predict the outcome of tool calls. If an agent is calling a weather API, you can show cached or estimated weather data while the real call completes. CoFounder's tool registry lets you define optimisticResult functions that produce placeholder data, which gets replaced transparently when the real result arrives.