Back to compare picker

Infinite spinner vs Loading skeleton vs Error state

Use a bounded loading treatment while work is genuinely pending, switch to skeleton for predictable content structure, and move to error or recovery when the operation fails or exceeds a useful wait.

Learn the decision

Match the interaction to the person's task

Use a bounded loading treatment while work is genuinely pending, switch to skeleton for predictable content structure, and move to error or recovery when the operation fails or exceeds a useful wait.

Infinite spinner

Use it when: Use this anti-pattern entry to audit loading, saving, syncing, uploading, report generation, billing retrieval, AI generation, and import flows that can hang.

Make this state clear: Initial pending state that names the operation and affected object.

Observe the result: The user can wait briefly, see that billing data is delayed, open cached values, retry once, or escalate with a reference.

Study the full pattern →

Loading skeleton

Use it when: The content shape is predictable.

Make this state clear: Initial skeleton placeholder state with region marked busy.

Observe the result: Users see that reports are pending, then can switch the demo to loaded, empty, or error outcomes on a bounded path.

Study the full pattern →

Error state

Use it when: A system or task failure blocks expected content or action.

Make this state clear: Normal expected state before failure.

Observe the result: User input and filter context are preserved after failure, and retry returns to recovered content or a clear still-failed state.

Study the full pattern →

Practice the judgment: Imagine this situation: The same region shows a spinner and skeleton, creating conflicting progress signals. Write the person's goal, choose one of these patterns, and show the state that helps the person continue. Use the decision rules and source trail below to explain your choice.

Review the underlying principle →

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.Keep skeleton dimensions stable, mark the affected region busy, and use subtle or reduced-motion-respecting animation only while loading is unresolved.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.Match the next action to the failure cause: retry transient failures, edit correctable input, use cached fallback for read-only continuity, or provide support reference when recovery is outside the UI.
Good UI Billing data is loading shows the affected account, elapsed wait, and actions for Retry, Use cached values, and Contact support after timeout.The interface shows a clear action and state.A failed save state keeps the draft visible and labels the support reference separately from the user-facing explanation.
Bad UI A Save button turns into a spinner forever, leaving the user unable to cancel, retry, or know whether the save happened.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 A failed backend response is hidden behind a permanent spinner.Large layout shift after content arrives.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.A specific progress indicator or blocking message is required.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 Give progress indicators accessible names that identify the operation or affected region.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 Save button turns into a spinner forever, leaving the user unable to cancel, retry, or know whether the save happened.
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
A failed backend response is hidden behind a permanent spinner.
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
Give progress indicators accessible names that identify the operation or affected region.
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
Keep skeleton dimensions stable, mark the affected region busy, and use subtle or reduced-motion-respecting animation only while loading is unresolved.
UX guidance
Reduce uncertainty and layout shift while predictable content loads, then resolve clearly to real content, empty state, or error state.
Good UI
The interface shows a clear action and state.
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
Large layout shift after content arrives.
Best fit
The content shape is predictable.
Avoid when
A specific progress indicator or blocking message is required.
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
Match the next action to the failure cause: retry transient failures, edit correctable input, use cached fallback for read-only continuity, or provide support reference when recovery is outside the UI.
Good UI
A failed save state keeps the draft visible and labels the support reference separately from the user-facing explanation.
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.

Saved items