Back to compare picker

Progress bar vs Loading spinner vs Loading skeleton vs Step navigation / step indicator

Choose progress bar when the system can expose percent complete, bytes transferred, item count, row count, batch count, or a bounded operation stage that moves toward a known finish.

Decision dimensions

Dimension Progress barLoading spinnerLoading skeletonStep navigation / step indicator
UI or UX UI + UX - Measurable system-operation progress indicatorUI + UX - Bounded indeterminate wait indicator for a named action or regionUI + UX - Bounded content loading placeholderUI + UX - Linear multistep task progress indicator
UI guidance Show a labeled bar with a track, filled value, and nearby helper text that reports the measurable unit such as percent, bytes, rows, files, records, or stages.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.Show a compact ordered list of named steps near the task heading, with visually distinct completed, current, upcoming, optional, and error states.
UX guidance Use a progress bar when the system can honestly report movement toward a known finish and users need to decide whether to wait, cancel, retry, continue elsewhere, or return later.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.Use step navigation to reduce uncertainty in long linear tasks by showing where users are, what is done, and what remains.
Good UI A document upload card says Uploading evidence.zip, shows 64%, 32 of 50 MB, a Cancel action, and keeps the rest of the form usable.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.A five-step application shows Eligibility complete, Contact details current, Documents upcoming, Review upcoming, and Submit upcoming, with the current step emphasized and a matching page heading.
Bad UI A blue bar fills across the top of the page with no label, no percent, and no affected object.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 two-page form adds a large stepper that consumes space without explaining meaningful progress.
Good UX A user uploads evidence.zip, sees progress move from 12% to 64%, cancels before commit, retries after a network error, and gets a completed receipt only after server processing succeeds.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.After a user enters valid contact details and continues, Contact details becomes complete and Documents becomes current.
Bad UX A fake progress bar inches to 99% for minutes with no elapsed time, cancel, retry, or background option.The spinner blocks the whole workspace for a small table refresh and prevents users from continuing other work.Skeleton never resolves.Clicking Review skips Documents, clears the contact form, and then blocks final submission without explaining the skipped prerequisite.
Best fit A system operation has a measurable total or bounded progress value.A short action, request, save, submit, refresh, sync, or fetch is actively processing and progress cannot be meaningfully measured.The content shape is predictable.A linear form, application, checkout, or setup flow has three or more meaningful steps.
Avoid when Progress cannot be measured or would be guessed.The content layout is predictable and a skeleton would better preserve structure.The system cannot predict the content layout.The journey has only one or two screens.
Required state Idle state before the operation starts.Idle state with no spinner and the action or region ready.Initial skeleton placeholder state with region marked busy.Default state with completed, current, and upcoming steps.
Accessibility burden Provide an accessible name that identifies the operation and affected object.Give the spinner or affected region an accessible name that identifies the operation.Mark the loading region busy or provide concise status text.Use aria-current on the current labeled step and include hidden or visible status text for completed, current, upcoming, optional, and error states.
Common misuse Fabricating progress values just to make users feel movement.Showing an unlabeled spinner on a blank page.Showing skeletons forever.Using a step indicator as breadcrumbs, tabs, side navigation, or pagination.

Progress bar

UI or UX
UI + UX - Measurable system-operation progress indicator
UI guidance
Show a labeled bar with a track, filled value, and nearby helper text that reports the measurable unit such as percent, bytes, rows, files, records, or stages.
UX guidance
Use a progress bar when the system can honestly report movement toward a known finish and users need to decide whether to wait, cancel, retry, continue elsewhere, or return later.
Good UI
A document upload card says Uploading evidence.zip, shows 64%, 32 of 50 MB, a Cancel action, and keeps the rest of the form usable.
Bad UI
A blue bar fills across the top of the page with no label, no percent, and no affected object.
Good UX
A user uploads evidence.zip, sees progress move from 12% to 64%, cancels before commit, retries after a network error, and gets a completed receipt only after server processing succeeds.
Bad UX
A fake progress bar inches to 99% for minutes with no elapsed time, cancel, retry, or background option.
Best fit
A system operation has a measurable total or bounded progress value.
Avoid when
Progress cannot be measured or would be guessed.
Required state
Idle state before the operation starts.
Accessibility burden
Provide an accessible name that identifies the operation and affected object.
Common misuse
Fabricating progress values just to make users feel movement.

Loading spinner

UI or UX
UI + UX - Bounded indeterminate wait indicator for a named action or region
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.
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.
Good UI
A Pay invoice button becomes Processing payment with a small spinner, disables only duplicate payment actions, and leaves the invoice reference visible.
Bad UI
A blank page shows a large spinner with no text, no affected object, and no idea what is loading.
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.
Bad UX
The spinner blocks the whole workspace for a small table refresh and prevents users from continuing other work.
Best fit
A short action, request, save, submit, refresh, sync, or fetch is actively processing and progress cannot be meaningfully measured.
Avoid when
The content layout is predictable and a skeleton would better preserve structure.
Required state
Idle state with no spinner and the action or region ready.
Accessibility burden
Give the spinner or affected region an accessible name that identifies the operation.
Common misuse
Showing an unlabeled spinner on a blank page.

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.

Step navigation / step indicator

UI or UX
UI + UX - Linear multistep task progress indicator
UI guidance
Show a compact ordered list of named steps near the task heading, with visually distinct completed, current, upcoming, optional, and error states.
UX guidance
Use step navigation to reduce uncertainty in long linear tasks by showing where users are, what is done, and what remains.
Good UI
A five-step application shows Eligibility complete, Contact details current, Documents upcoming, Review upcoming, and Submit upcoming, with the current step emphasized and a matching page heading.
Bad UI
A two-page form adds a large stepper that consumes space without explaining meaningful progress.
Good UX
After a user enters valid contact details and continues, Contact details becomes complete and Documents becomes current.
Bad UX
Clicking Review skips Documents, clears the contact form, and then blocks final submission without explaining the skipped prerequisite.
Best fit
A linear form, application, checkout, or setup flow has three or more meaningful steps.
Avoid when
The journey has only one or two screens.
Required state
Default state with completed, current, and upcoming steps.
Accessibility burden
Use aria-current on the current labeled step and include hidden or visible status text for completed, current, upcoming, optional, and error states.
Common misuse
Using a step indicator as breadcrumbs, tabs, side navigation, or pagination.
Decision rules
  • Choose progress bar when the system can expose percent complete, bytes transferred, item count, row count, batch count, or a bounded operation stage that moves toward a known finish.
  • Choose loading spinner when the operation is active but progress cannot yet be calculated and the expected wait is short enough for a compact indeterminate indicator.
  • Choose loading skeleton when the pending result has predictable layout and the user benefits from reserved card, row, or text structure before content arrives.
  • Choose step navigation when progress comes from a person completing named journey steps, not from system work such as upload, download, validation, processing, or import.
  • Do not show a percent bar if the value is fabricated, jumps backward, resets during one operation, or reaches 100% before the operation can honestly finish.
  • Switch from spinner or indeterminate bar to progress bar when the backend exposes enough information to report a meaningful value.
  • Switch from progress bar to success, error, retry, cancel, or background job status when the operation completes, fails, stalls, or is no longer worth blocking the current surface.
  • Use a progress bar inside the smallest affected scope: file row, import card, report job panel, dialog, or page region; do not block unrelated work unless the whole surface depends on the operation.
Inspect live examples
Failure modes
  • An upload bar reaches 100% while server scanning is still running, so users leave before the attachment is accepted.
  • A progress bar counts upward with no file size, row count, or stage data and then snaps backward after a retry.
  • A batch import has a measurable valid-row count but uses only a spinner, hiding whether work is moving.
  • A progress bar is used as a checkout stepper, confusing system processing with user task progress.
  • The label is far from the bar, so users cannot tell whether it belongs to the file upload, report export, or page load.
  • A failed operation leaves the bar active instead of showing a persistent error and recovery path.