Optimistic UI updates with WebSockets
optimistic update pattern, client-side state mutation, server confirmation, rollback on failure, message ID tracking, UI state reconciliation, latency hiding
Show the Update Before the Server Confirms It
Waiting for a server round trip before updating the UI adds 50–300ms of visible lag. Optimistic updates apply the change locally first, then confirm or roll back based on the server response:
// Assign a client-generated ID to each message function sendMessage(text) { const tempId = crypto.randomUUID(); // 1. Show immediately addMessageToUI({ id: tempId, text, status: 'sending' }); // 2. Send to server socket.emit('chat:send', { tempId, text }, (response) => { if (response.ok) { // Replace temp with server-confirmed message updateMessage(tempId, { id: response.id, status: 'sent' }); } else { // Roll back removeMessage(tempId); showError('Failed to send message'); } }); }
What Can Go Wrong
Optimistic updates break when: the server rejects the action due to validation (show an error, roll back); the network drops and the ack never arrives (use timeout on ack, roll back after N seconds); or two clients edit the same resource simultaneously (the server must detect conflict and tell clients to reconcile). For simple chat, optimistic updates are safe. For collaborative editing, you need operational transforms or CRDTs to resolve conflicts.
