01 — Foundation

Async actions need visible state

Every server round-trip should answer: did it start, is it still running, and what happened?

Async actions — form submits, API calls, background updates — are where double clicks, duplicate charges, and lost focus cause the most damage. The UI must communicate state on the control that triggered the action.

02 — Controls

Buttons and forms during requests

Disable with purpose, label clearly, and restore on failure.

  • disable the triggering control while the request is in flight — or the whole form when needed
  • change label — “Submitting…”, “Processing payment…” — not a spinner alone
  • prevent double submission — disable submit, debounce, or idempotent server handling
  • restore controls and focus on error so users can fix and retry
<button type="submit" disabled aria-busy="true">
    Saving…
</button>

03 — Feedback

Success, failure, and partial completion

Inline feedback beats mystery spinners that vanish without explanation.

  • success — inline confirmation or status region; see Confirmation
  • failure — specific message and recovery; see Error States
  • long tasks — progress or step text; see Loading States
  • optimistic UI only when rollback is safe and errors are obvious

04 — Accessibility

Keyboard and assistive technology

Busy state must be programmatic, not only visual grey-out.

  • aria-busy="true" on the relevant region while content updates
  • do not move focus unexpectedly when a request completes
  • announce outcomes with live regions — sparingly, and in context

05 — Review

Before you approve

A short checklist for async actions in code review.

  • in-flight state is visible; double submit is prevented
  • success and failure are specific and recoverable
  • offline and timeout cases are handled — see Offline States