A useful SaaS loading state tells people what is happening, whether they can leave, and what they can do next. Show success only when the promised result is ready. Accepting a request and finishing the work are different events.
Consider a fictional app that imports a contact list. The browser can finish uploading the file while the server is still checking rows. A cheerful “Done” at that point makes the screen describe a result the app hasn't produced yet.
Name the stage the user is waiting for
Give each stage a plain label. For this import, that might be “Uploading file,” “Checking rows,” and “Adding contacts.” Keep the label close to the action or result it describes.
Avoid showing a percentage unless it represents measured work. If the app knows that 80 of 200 rows have been checked, it can report that count. If it only knows that a job is waiting to start, “Waiting to start” is more honest than a slowly moving bar.
MDN's progressbar reference explains that an indeterminate progress bar has no current numeric value. It also recommends native HTML progress elements where suitable. Match the accessible value to the progress you can actually measure.
Define what finished means
Before choosing an animation, write the success condition in a sentence. For our example: “The accepted contacts are saved and available in the contact list.”
Then decide how the screen describes a partial result. If 190 rows were added and 10 need changes, show both counts and a way to inspect the rejected rows. Calling the entire job successful hides work the person still needs to do.
Treat these as separate states in the product design:
- Waiting for work to begin.
- Work in progress.
- Finished with the full result.
- Finished with items that need attention.
- Failed, with a clear next action.
The exact states depend on the task. A payment, file upload, and report build should each describe their own real outcome.
Explain whether leaving is safe
Only say “You can close this page” if the job will continue and the person has a reliable way to find the result later. In the import example, that could be an import history page with the same job and its latest status.
If closing the page cancels an upload, explain that while the upload is running. Once the server has accepted the file and taken over the job, update the message to reflect that change.
A reload should restore the actual state when the product supports background work. Replacing an active job with an empty screen gives the user no useful account of what happened.
Make status changes available to screen readers
W3C's guidance on status messages covers changes such as waiting, progress, success, and errors that do not move focus. These messages need to be available to assistive technology through appropriate roles or properties.
Announce meaningful changes and test the result with a screen reader. Avoid moving keyboard focus merely to point at an ordinary progress update. Also avoid turning every tiny progress change into a stream of announcements.
Test the awkward endings
Try a slow connection, a failed request, a reload during processing, and a job with some invalid rows. Check what happens when someone clicks again because the first click appeared to do nothing.
For each case, ask whether the screen tells the truth and offers a safe next step. If a retry could repeat completed work, fix that behavior before presenting retry as the answer.
The final message should name the result: “190 contacts added. Review 10 rows.” Now the person knows where the work stands and what remains.
Hey I'm Uriel Bitton. I write about building in public strategies and growing startups.
Subscribe for more stories on growing your audience by building in public.
Join us on Buildside: the social network for founders building in public.
Top comments (0)