One mis-click on a delete button that wipes a forty-message research conversation is the kind of moment that ends a subscription. Destructive actions without undo treat every user gesture as final, which is wrong for a surface where the data is valuable, the buttons are dense, and mobile accidental taps are routine. A simple 5-second undo toast costs almost nothing to build and prevents the single worst user experience your product can produce.
Medium because the loss is recoverable only if you have backups, and most chat apps do not expose them.
Wrap every destructive action in an optimistic state update plus a toast with an "Undo" action and a 5-second duration. Keep the deleted record in memory during the window and re-insert at its original index on undo. For heavier flows (bulk clear), move to a two-step confirm dialog. Implement in src/lib/hooks/use-undoable-delete.ts.
toast('Message deleted', { action: { label: 'Undo', onClick: () => restore(deleted) }, duration: 5000 })
ID: ai-ux-patterns.feedback-control.undo-redo-actions
Severity: medium
What to look for: Count all destructive conversation actions (message deletion, conversation clearing, edit-and-regenerate). For each, enumerate whether an undo mechanism exists: Cmd+Z handler, "restore" toast, undo button, or conversation version history. Look for state history arrays or a history management hook (e.g., useReducer with an action history, a zustand store with undo middleware, or a custom history stack). At least 1 destructive action must have an undo path.
Pass criteria: Users can undo at least the most recent destructive action in a conversation (message deletion or edit). A "restore" toast with an undo button after deletion is an acceptable implementation. At least 1 undo mechanism must be wired to a destructive action. Report on pass: "X of Y destructive actions have undo support."
Fail criteria: Destructive actions (deleting messages, clearing a conversation) are permanent with no undo option. No history management detected.
Skip (N/A) when: Same as regeneration-button.
Detail on fail: "No undo mechanism for message operations — deletions appear permanent. No history stack or undo toast pattern detected.".
Remediation: Accidental message deletions are a common frustration. A simple undo-toast pattern is low effort and high value:
const handleDeleteMessage = (messageId: string) => {
const deletedMessage = messages.find(m => m.id === messageId)
const newMessages = messages.filter(m => m.id !== messageId)
setMessages(newMessages)
toast("Message deleted", {
action: {
label: "Undo",
onClick: () => setMessages(prev => {
const index = newMessages.findIndex(/* insertion point */)
return [...prev.slice(0, index), deletedMessage, ...prev.slice(index)]
})
},
duration: 5000,
})
}