| UI or UX | UI + UX - Bounded indeterminate wait indicator for a named action or region | UI + UX - Bounded content loading placeholder | UI + UX - Unbounded loading anti-pattern | UI + UX - Recoverable failure surface |
| UI guidance | Render a compact spinner only beside, inside, or over the affected action, component, or page region, and pair it with concise text that names what is loading or processing. | Render neutral skeleton blocks that reserve the final content structure without fake readable text, fake controls, or focusable placeholder elements. | Replace an unlabeled endless spinner with a bounded loading surface that names the operation, preserves the affected context, and changes state after the wait threshold. | Render a persistent error region near the affected content with a specific failure heading, plain-language cause, preserved context, and recovery actions. |
| UX guidance | Use a loading spinner for short indeterminate waits where the system is actively working but cannot yet expose progress; resolve it quickly to content, success, cancellation, progress, or error. | Reduce uncertainty and layout shift while predictable content loads, then resolve clearly to real content, empty state, or error state. | Help users decide whether to wait, retry, leave safely, use stale data, or escalate instead of forcing them to infer system health from animation alone. | Help users recover when expected loading, saving, validation, sync, permission, or computation fails without losing their work. |
| Good UI | A Pay invoice button becomes Processing payment with a small spinner, disables only duplicate payment actions, and leaves the invoice reference visible. | Three report-card placeholders match the final card heights and are replaced by real cards without shifting the panel. | Billing data is loading shows the affected account, elapsed wait, and actions for Retry, Use cached values, and Contact support after timeout. | Reports could not load appears in the report section with the saved filter, Retry, Use cached data, and Contact support actions. |
| Bad UI | A blank page shows a large spinner with no text, no affected object, and no idea what is loading. | Skeleton rows look like clickable report cards and receive focus before content exists. | A centered spinner animates on a blank page for minutes with no label or escape. | Tiny transient toast for a blocking failure. |
| Good UX | After submit, users see payment PAY-2048 processing, can tell the button is temporarily unavailable, and then get either success or retry guidance. | Users see that reports are pending, then can switch the demo to loaded, empty, or error outcomes on a bounded path. | The user can wait briefly, see that billing data is delayed, open cached values, retry once, or escalate with a reference. | User input and filter context are preserved after failure, and retry returns to recovered content or a clear still-failed state. |
| Bad UX | The spinner blocks the whole workspace for a small table refresh and prevents users from continuing other work. | Skeleton never resolves. | Users refresh the page because the spinner never explains whether the request is still working or broken. | Clearing work after save failure. |
| Best fit | A short action, request, save, submit, refresh, sync, or fetch is actively processing and progress cannot be meaningfully measured. | The content shape is predictable. | Use this anti-pattern entry to audit loading, saving, syncing, uploading, report generation, billing retrieval, AI generation, and import flows that can hang. | A system or task failure blocks expected content or action. |
| Avoid when | The content layout is predictable and a skeleton would better preserve structure. | The system cannot predict the content layout. | A short spinner is clearly labeled, tied to a specific action, and guaranteed to transition quickly. | Nothing exists yet and the state is expected. |
| Required state | Idle state with no spinner and the action or region ready. | Initial skeleton placeholder state with region marked busy. | Initial pending state that names the operation and affected object. | Normal expected state before failure. |
| Accessibility burden | Give the spinner or affected region an accessible name that identifies the operation. | Mark the loading region busy or provide concise status text. | Do not leave a region or page in a permanent busy state after loading has stalled or failed. | Use appropriate alert or status semantics for newly appearing critical errors. |
| Common misuse | Showing an unlabeled spinner on a blank page. | Showing skeletons forever. | Animating a full-page spinner forever after a request times out. | Using a transient toast for critical errors. |