Skip to content

Optimistic UI for AI agents is safe only where undo is real

AI · November 10, 2026 · 9 min read

Glowing outlines on black: a row of five tiles on a line, the first three lit white with a curved arrow looping back over them and the third inside a three-quarter ring, then two tall white bars like a closed gate, and two faint grey tiles waiting beyond it with nothing above them

Optimistic UI for AI agent actions is a feedback pattern that shows the result of an agent's work before the server confirms it, and it stays honest only when the product can take that result back. A like button can flip to "liked" in 50 milliseconds because the request almost never fails and a failure costs nothing. An agent that refunds an order, emails the customer and closes the ticket does none of that in 50 milliseconds. It fails more often, and it leaves effects outside your product that no state rollback can reach.

This piece is for product teams putting agents inside a SaaS who want the screen to move the moment the user asks, without ever showing "Done" for something that has not happened. The rule we work by fits in one line: show intent at once, and show an effect only after it happened.

Why does an agent break optimistic UI?

Classic optimistic UI rests on three assumptions. The server answers fast. The request almost always succeeds. A failure is cheap to undo, because undoing it means putting the old value back on screen. Agent actions break all three.

Speed first. Nielsen Norman Group's three response time limits still frame the problem: 0.1 seconds feels like the user caused the result, 1 second keeps the flow of thought, and 10 seconds is about the limit for keeping attention on the task. Past that, the same article says users need feedback on when the work will finish, and that feedback matters most when the response time is highly variable. An agent run is the definition of variable. The same request can take one tool call or twelve.

Success rate second. A run fails when the model misreads the request, when a tool times out, when a permission is missing. Each step is a new chance to fail, so a seven-step run fails more often than a single write to your own database.

Rollback third, and this is the one that hurts. React's useOptimistic shows a temporary value while an Action is in progress. If the Action throws, the Transition ends and React renders the real value again, so the optimistic item disappears. The documentation treats telling the user about the failure as your job. The screen goes back; the explanation does not come with it. And when the step was an email that already left, putting the old value back on screen changes nothing in the customer's inbox.

Why a spinner does not fix it

The common retreat is a single "Thinking..." indicator for the whole run. It is honest and it is useless. Forty seconds of the same animation tells the user nothing about which step is running, gives them nothing to cancel, and invites a second click, which starts a second run. Showing "Done" early is useful and false. The pattern below sits between the two.

Sort every agent action by what undo costs

Before designing any screen, sort the actions the agent can take into three classes. The class decides what the interface may show before confirmation.

ClassExamplesWhat the screen shows at onceHow undo works
Local and reversibleDrafting text, tagging a record, reordering a list inside the productThe result, immediatelyRestore the previous state and say why
Deferred commitSending an email, posting a message, scheduling a meetingThe effect as pending, with a countdown and UndoCancel before the window closes; nothing has left yet
External and irreversibleCharging or refunding a card, deleting data in another systemThe plan, then a confirmation, then live progressNone after commit; only a second, compensating action

Most of the trouble comes from treating a class 3 action with class 1 feedback. The six practices that follow apply the table.

1. Be optimistic about the plan

Within 100 milliseconds of the request, render the agent's plan as a list of steps marked "planned". This claims something true: the request arrived and the agent has a way to handle it. It claims nothing about results. Victor Yocco's patterns for agentic AI in Smashing Magazine call this an Intent Preview and place it before the agent acts, next to an Autonomy Dial that sets how much the agent may do unasked. If the model has not produced a plan yet, echo the request itself as the first line. The screen moves, and nothing on it is false.

2. Give each step its own state

Use five states per step: planned, running, done, failed, skipped. Stream them from the server as each step changes, so the list fills in while the user watches. A run of seven steps then reads "step 3 of 7, looking up the order", which answers the NN/g question of when the work will end better than a percent bar can when nobody knows how long step 5 will take. A single spinner hides the one thing the user needs when a run fails: where it stopped.

3. Hold a commit window for anything that leaves the product

Gmail's Undo Send lets users pick a cancellation period of 5, 10, 20 or 30 seconds. Its undo is real because the message has not been sent yet: Gmail waits before delivering it. Agents need the same mechanism. Queue the outbound effect, show it as "Sending to the customer in 10 s" with an Undo button, and commit when the timer ends. The undo works because nothing has happened. A recall after delivery is a different, much weaker feature.

4. Confirm before effects you cannot reverse

OpenAI built this into Operator: the agent asks for confirmation before finalizing tasks with external effects, such as submitting an order or sending an email. Microsoft's Guidelines for Human-AI Interaction put the general case in G10, scope services when in doubt: when the system is unsure of the user's goal, it asks before it acts. An irreversible effect is where that doubt costs the most. The confirmation names the exact effect: the amount, the recipient, the account. For a batch, ask once and list every item, so a run of 40 refunds is one decision and not 40 interruptions.

5. Make the retry safe before you make the button fast

Fast feedback invites retries, and a user who is unsure clicks again. Give every agent action an idempotency key, created when the plan is created, so a retried run carries the same key. Stripe's idempotent requests show the contract: a retry with the same key within 24 hours returns the first result, and keys can be removed after that. Without a key, an optimistic screen plus a nervous user is a double refund.

6. When a step fails, reverse it on screen and say what happened

Microsoft's guideline G9 is "support efficient correction": make it easy to edit, refine or recover when the system is wrong. In an agent run that means the failed step turns red with the reason in plain words, the steps already done stay marked done (the email went, the refund did not), the dependent steps move to skipped, and one action is offered next: retry the step, edit the request, or hand it to a person. Our piece on error state UX covers the wording.

What this looks like on a real screen

Take a hypothetical support agent inside a helpdesk product. The user types "Refund order 4417 and let the customer know."

  1. At 0.1 seconds a plan appears: look up order 4417, refund €84.00 to the card on file, email the customer, close the ticket. All four steps read "planned".
  2. Step 1 runs and turns done in about two seconds. It only reads data, so nothing needs guarding.
  3. Step 2 stops on a confirmation that names the amount and the last four digits of the card. The user confirms. The refund goes out with the idempotency key created with the plan, and the step turns done when the payment provider confirms the refund, not when the request leaves.
  4. Step 3 shows "Sending to the customer in 10 s" with Undo. The email waits in a queue behind step 2.
  5. Step 4 closes the ticket. It lives inside the product and can be reopened, so the screen shows it closed at once.

If the refund fails, step 2 turns failed with the provider's reason, steps 3 and 4 move to skipped, and the email never leaves, because it was queued behind a step that did not finish. The user sees exactly one thing to decide.

What the pattern costs

Three costs, all worth knowing before you commit to it. A commit window adds its length to every send, so a 10-second window means every email the agent writes arrives 10 seconds later. Confirmations slow down the people who use the agent most; the fix is an autonomy setting that lets a team pre-approve a class, such as refunds under €50, and keep confirmations for the rest. And streaming step states needs a backend that emits events per step, which is more work than one request and one response. If your agent architecture is still a single prompt behind a button, our notes on embedding AI agents in SaaS cover the decisions that come first.

The pay-off is a screen that moves in 100 milliseconds and never says anything the system will have to take back.

Sources

Frequently asked questions

Rather have it done for you?

We design and build this kind of work for clients. Tell us what you need.