---
title: "Use toasts only to confirm finished work · Agent Wranglers"
url: https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs/toasts-for-status
updated: 2026-10-20
licence: "All rights reserved"
---

# Use toasts only to confirm finished work

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Feedback and dialogs](https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs)
- Rule: Use a toast only to confirm that something worked, such as "Invoice sent". Announce it through a live region, keep it for at least 6 seconds, and pause it while hovered or focused.
- How: Write toasts into one region with `role="status"` that is in the page from the start, as [status messages](https://agentwranglers.se/playbook/website/accessibility/status-messages) requires. Never move focus to a toast. Give it a close button. If a toast holds an action, such as Undo, keep it until closed or offer the same action elsewhere, because [time limits](https://agentwranglers.se/playbook/website/accessibility/time-limits) apply. Show at most 3 at once.
- Check: A browser test sends an invoice. The status region, present before the click, holds the message. Focus stays on the button, and the toast is still there after 5 seconds. While the pointer rests on it, it is still there after 10 seconds.

Example:

```ts
const MIN_VISIBLE_MS = 6000;
const region = document.querySelector<HTMLElement>('#toasts')!;

export function toast(message: string) {
  const item = Object.assign(document.createElement('p'), { textContent: message, className: 'toast' });
  region.append(item);
  let remaining = MIN_VISIBLE_MS;
  let startedAt = Date.now();
  let timer: ReturnType<typeof setTimeout> | undefined = setTimeout(() => item.remove(), remaining);
  const pause = () => {
    if (!timer) return;
    clearTimeout(timer);
    timer = undefined;
    remaining -= Date.now() - startedAt;
  };
  const resume = () => {
    if (timer || item.matches(':hover, :focus-within')) return;
    startedAt = Date.now();
    timer = setTimeout(() => item.remove(), remaining);
  };
  for (const event of ['pointerenter', 'focusin']) item.addEventListener(event, pause);
  for (const event of ['pointerleave', 'focusout']) item.addEventListener(event, () => setTimeout(resume));
}
```

```python
import json

@app.post("/invoices/<invoice_id>/send")
def send_invoice(invoice_id):
    invoices.send(current_user().account_id, invoice_id)
    response = make_response(render_template("invoices/_status.html", status="sent"))
    response.headers["HX-Trigger"] = json.dumps({"toast": {"message": "Invoice sent"}})
    return response
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs/toasts-for-status`.

- Next: [Keep errors that need action on screen](https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs/errors-stay-on-screen)
- [Feedback and dialogs](https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs)
