Back to compare picker

Infinite spinner vs Loading skeleton vs Error state

Flag infinite spinner when the interface can remain busy with no named operation, elapsed status, timeout, retry, cancellation, cached fallback, or failure state.

Decision dimensions

Dimension Infinite spinnerLoading skeletonError state
UI or UX UI + UX - Unbounded loading anti-patternUI + UX - Bounded content loading placeholderUI + UX - Recoverable failure surface
UI guidance 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 neutral skeleton blocks that reserve the final content structure without fake readable text, fake controls, or focusable placeholder elements.Render a persistent error region near the affected content with a specific failure heading, plain-language cause, preserved context, and recovery actions.
UX guidance 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.Reduce uncertainty and layout shift while predictable content loads, then resolve clearly to real content, empty state, or error state.Help users recover when expected loading, saving, validation, sync, permission, or computation fails without losing their work.
Good UI Billing data is loading shows the affected account, elapsed wait, and actions for Retry, Use cached values, and Contact support after timeout.Three report-card placeholders match the final card heights and are replaced by real cards without shifting the panel.Reports could not load appears in the report section with the saved filter, Retry, Use cached data, and Contact support actions.
Bad UI A centered spinner animates on a blank page for minutes with no label or escape.Skeleton rows look like clickable report cards and receive focus before content exists.Tiny transient toast for a blocking failure.
Good UX The user can wait briefly, see that billing data is delayed, open cached values, retry once, or escalate with a reference.Users see that reports are pending, then can switch the demo to loaded, empty, or error outcomes on a bounded path.User input and filter context are preserved after failure, and retry returns to recovered content or a clear still-failed state.
Bad UX Users refresh the page because the spinner never explains whether the request is still working or broken.Skeleton never resolves.Clearing work after save failure.
Best fit Use this anti-pattern entry to audit loading, saving, syncing, uploading, report generation, billing retrieval, AI generation, and import flows that can hang.The content shape is predictable.A system or task failure blocks expected content or action.
Avoid when A short spinner is clearly labeled, tied to a specific action, and guaranteed to transition quickly.The system cannot predict the content layout.Nothing exists yet and the state is expected.
Required state Initial pending state that names the operation and affected object.Initial skeleton placeholder state with region marked busy.Normal expected state before failure.
Accessibility burden Do not leave a region or page in a permanent busy state after loading has stalled or failed.Mark the loading region busy or provide concise status text.Use appropriate alert or status semantics for newly appearing critical errors.
Common misuse Animating a full-page spinner forever after a request times out.Showing skeletons forever.Using a transient toast for critical errors.

Infinite spinner

UI or UX
UI + UX - Unbounded loading anti-pattern
UI guidance
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.
UX guidance
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.
Good UI
Billing data is loading shows the affected account, elapsed wait, and actions for Retry, Use cached values, and Contact support after timeout.
Bad UI
A centered spinner animates on a blank page for minutes with no label or escape.
Good UX
The user can wait briefly, see that billing data is delayed, open cached values, retry once, or escalate with a reference.
Bad UX
Users refresh the page because the spinner never explains whether the request is still working or broken.
Best fit
Use this anti-pattern entry to audit loading, saving, syncing, uploading, report generation, billing retrieval, AI generation, and import flows that can hang.
Avoid when
A short spinner is clearly labeled, tied to a specific action, and guaranteed to transition quickly.
Required state
Initial pending state that names the operation and affected object.
Accessibility burden
Do not leave a region or page in a permanent busy state after loading has stalled or failed.
Common misuse
Animating a full-page spinner forever after a request times out.

Loading skeleton

UI or UX
UI + UX - Bounded content loading placeholder
UI guidance
Render neutral skeleton blocks that reserve the final content structure without fake readable text, fake controls, or focusable placeholder elements.
UX guidance
Reduce uncertainty and layout shift while predictable content loads, then resolve clearly to real content, empty state, or error state.
Good UI
Three report-card placeholders match the final card heights and are replaced by real cards without shifting the panel.
Bad UI
Skeleton rows look like clickable report cards and receive focus before content exists.
Good UX
Users see that reports are pending, then can switch the demo to loaded, empty, or error outcomes on a bounded path.
Bad UX
Skeleton never resolves.
Best fit
The content shape is predictable.
Avoid when
The system cannot predict the content layout.
Required state
Initial skeleton placeholder state with region marked busy.
Accessibility burden
Mark the loading region busy or provide concise status text.
Common misuse
Showing skeletons forever.

Error state

UI or UX
UI + UX - Recoverable failure surface
UI guidance
Render a persistent error region near the affected content with a specific failure heading, plain-language cause, preserved context, and recovery actions.
UX guidance
Help users recover when expected loading, saving, validation, sync, permission, or computation fails without losing their work.
Good UI
Reports could not load appears in the report section with the saved filter, Retry, Use cached data, and Contact support actions.
Bad UI
Tiny transient toast for a blocking failure.
Good UX
User input and filter context are preserved after failure, and retry returns to recovered content or a clear still-failed state.
Bad UX
Clearing work after save failure.
Best fit
A system or task failure blocks expected content or action.
Avoid when
Nothing exists yet and the state is expected.
Required state
Normal expected state before failure.
Accessibility burden
Use appropriate alert or status semantics for newly appearing critical errors.
Common misuse
Using a transient toast for critical errors.
Decision rules
  • Flag infinite spinner when the interface can remain busy with no named operation, elapsed status, timeout, retry, cancellation, cached fallback, or failure state.
  • Use a short labeled spinner when an action is actively processing, expected to finish quickly, and does not require the user to make another decision while waiting.
  • Use loading skeleton when predictable content layout is pending and the placeholder will resolve to loaded content, empty state, error state, or cancelled state.
  • Use determinate progress, step status, or queue position when the backend can expose percent complete, stages, item counts, or job order.
  • Use error state when loading, saving, syncing, importing, or report generation fails, times out, loses permission, or reaches a retry limit.
  • After the wait threshold, offer recovery that matches the operation: retry for transient network failure, cancel for user-started jobs, cached data for read-only loads, background continuation for long jobs, or support reference for unrecoverable stalls.
  • Do not combine spinner and skeleton for the same region unless each indicator describes a different piece of work with a separate resolution path.
  • Do not keep a button disabled with only a spinner when users need to correct input, cancel, retry, or leave safely.
Inspect live examples
Failure modes
  • A spinner masks an API failure and the page never transitions to an error state.
  • A skeleton screen shimmers forever because no timeout path exists.
  • The same region shows a spinner and skeleton, creating conflicting progress signals.
  • A disabled loading button prevents cancellation or correction after the request has stalled.
  • Users refresh and duplicate a submission because the UI never confirms success or failure.
  • A busy region is never cleared for assistive technology after the operation fails.