Key insights
- Match the error type to its surface. A validation error belongs inline under the field, a lost connection reads as a banner or toast, and a server crash or permission block needs its own prominent space — each type has a natural home.
- Let severity drive the surface. Minor issues stay inline, transient ones surface as a toast, and blocking failures earn a modal. The more an error interrupts the user, the more space it should occupy — and the reverse.
- Every error needs an exit. A dead-end 'OK' button leaves people stuck. Give a real way out — a Retry, a link to support, or expandable technical details for those who want to dig in.
- Write copy for humans, not machines. 'Error 500 — An error occurred' tells the user nothing. Say what broke, why, and what to do next in plain, specific language.
- Prevent errors before they happen. Live inline validation — checking each rule as the user types and turning criteria green — stops most mistakes before submit. Validating only on submit just tells people they failed after the fact.
- Keep field-level validation small, inline, and specific — anchored to the input it describes, not floating in a generic alert.
Do / Don't
- Do: Offer a clear recovery action on every error — retry, undo, or a path to help.
- Do: Match the surface to severity: inline for field errors, toast for transient issues, modal for true blockers.
- Do: Validate inline as the user types so problems surface before submit.
- Don't: Ship dead-end errors whose only option is 'OK'.
- Don't: Surface raw codes like 'Error 500' or 'An error occurred' with no guidance.
- Don't: Interrupt a minor validation slip with a full-screen modal.