Hacker News new | past | comments | ask | show | jobs | submit login
UX design patterns for loading (pencilandpaper.io)
140 points by akbarnama on Aug 27, 2023 | hide | past | favorite | 69 comments



Is it just me, or skeleton screens are annoying as hell?

I instinctively interpret them as a malfunction, not as a loading screen. A screen is supposed to have data. If it shows up empty, something has gone horribly wrong somewhere.

And as a developer, once it fills out, I get the secondary tinge of discomfort due to the nagging thought "this application has a bad handle on timing, there might be a race condition somewhere. Better do things really slowly and carefully."

In general though, 99% of the time, there should be no loading screen at all. My local airline shows a "Loading..." for several seconds even when doing things like logging in, and all I can think of is wondering what could take so much time. At this point a login should be a thing that can be done in a few ms.


What do you propose instead? Just a spinner? No indication that something is happening at all (browser default)?

I think placeholder components are a step above “Loading…” because they hint at what’s to come and confirm that you’re about to go to the screen you expect. They also indicate that the user is experiencing a view change which is more specific than "wait while something is happening." It's more info.

As for why we need loading screens at all, plenty of developers make the mistake of never considering loading UX because they only use their own app from an optimal internet connection, so they never consider the experience from worse connections.


Apple Musics approach is my favorite.

They show a white screen for half a second, which is usually enough to load the content. If the request takes too long, a spinner fades in.

They also, very conservatively, show partial- or skeleton screens. Crucially, these skeleton never animate, cause layout shifts or show filler content. They usually just show UI elements that would be there regardless of what the loaded content is.

I never even noticed the loading states until I started paying attention to them.


> What do you propose instead? Just a spinner? No indication that something is happening at all (browser default)?

Sure, that works. If that's too slow, it should be made faster.

I mean, maybe that sounds odd, but even on say, a 10 Mbps connection you can load stuff at 1 MB/s, which means you can load 100K in 100 ms. That's plenty, considering in modern times we don't reload the whole page, so stuff can be preloaded and cached.


I would've guessed that it's normal to have bad connection often when on mobile. At home I have a stable and high bandwith connection, but on my phone when commuting I regularly loose connection for seconds or minutes at a time. Designign apps should take this into account (that you can't completely optimize out of a bad connection)


Oh, sure. I get having it as a fallback for the rare cases. But there's stuff that performs horribly in optimal conditions.

Eg, my airline has this "Login" link on the front page. If I click that, it pops up a little box, then for some bizarre reason has a "Loading..." inside that shows up for 2 seconds, then finally shows a field for username and password. This is on a 16 core CPU with a 1 Gbps fiber connection.

Like what is there even to load? It's just 2 text fields and a couple buttons!

If it's some tracking BS, well, that could be done asynchronously in the background.

I get that there still exist slow computers and bad connections, but in 99.9% of cases, there's no reason to have a visible load time on anything even relatively modern. We've long had the bandwidth to load more than anyone would care to read in 10ms, and the CPU horsepower to deal with it.


I hate skeleton loaders. I usually see them implemented to "prevent" async-loaded elements from causing layout shift... the problem is the backend isn't set up to tell you how many things need to be loaded, what the size of those things are, so the skeleton just assumes some arbitrary values and sizes, takes up space in the layout, and when the loading finishes the layout still inevitably shifts, so the skeleton didn't even do its perceived job except make loading "prettier" (to someone obviously not me).


They cause me to defect. I used to use CNN.com every morning to check stock futures. But the skeleton loading screen takes an incredibly variable amount of time. Sometimes, it’s just a second, but often it’s longer. I don’t feel like sitting there waiting to find out so I switched to CNBC.com, which doesn’t seem to have that problem.


I am not a UI designer, but I don't know why content loads without changing the layout and then has a gentle animation to expand to the appropriate size.

the sudden shift is jarring, especially if the content is staggered


Minimal has a nice trick for loading new collaborations… When joining a note a writer is met with a simple presentation of the note title, the collaborator’s name, and a small bit of text describing how collaboration works. By the time the writer has processed that minuscule yet valuable information, the note has loaded and is ready for them.

Effect: - no wait time, snappy interaction - better context around a new experience

Cost: - nothing

It reminds me that a solid portion of good design is not applying a rule or system, but creatively working between the cracks of known/obvious problem-solution pairs.


Reminds me of an apocryphal story of the engineer tasked with speeding up elevators in a tall hotel. After evaluating all the options, they ended up adding mirrors next to the elevators. The guest complaints about the slowness of the elevators decreased as people pushed the button to request the elevator, noticed their reflection, checked their hair, and before they knew it the elevator had arrived.

(I do not know where this story originated, but ever since hearing about this from one of my design professors I've noticed how many buildings put mirrors next to their elevators.)


I know this is apocryphal because I’ve never met a single engineer who’d identify that kind of human-centric solution.


Rubbish. Good engineers will think laterally about what they can do to make the product work better for the customer, and this is especially true they're in a tight technical spot. Everyone can get railroaded by their thought process and be blinded to the easy, simple solutions, but a good engineer is supposed to be aware of that.

More likely, the lift engineer (or team) is only allowed to "make this lift 20% faster" and is told to stay in their lane when they suggest non-lift-based ideas.

In all likelihood, the engineer is employed by a company in order to sell expensive lift upgrades, not mirrors or interior design services. And his boss has a target.


There's a similar bit in the book Alchemy by Rory Sutherland where he talks about trying to increase trains reliability or the satisfaction from a gas engineer callout. Turns out people just really don't like uncertain waiting. Making trains on time or predicting when an engineer will arrive has too many variables to really solve. But a live display of trains with delay times, a expected arrival window with promise of a call 20 mins before arriving, is enough people know if they can go to the loo, or nip to the shop etc. Live train boards can have a much greater impact on traveler reliability ratings then actually making trains more reliable.

Sometimes solving the impact of a problem is much easier then solving the problem.


I do wonder how many train passengers would prefer a system where trains were randomly between 0 and 1 minutes late with no information to guide them on expected arrival time, and one where trains were randomly between 1 and 60 minutes late with reliable information detailing how late they were each time.


It's about uncertainty though so it doesn't really work if you know it will turn up (also 1 minute is hardly a delay).

People would probably choose a system of 0-30mins delay with no info vs 0-60mins with info, if they know thats the rules. 'it'll be here within half an hour' is close enough to knowledge to get the uncertainty out the way which is a lot of the problem.

But if passengers don't know what the rules are or when (or if) the train is coming, and you'll get a lot less complaints and grumbling passengers with a 0-60 full info delay system then you do with a 0-20 with no info. In one you know the train is coming in 47mins. Annoying, but you can tune out for 30 mins, get a coffee, read hacker news, tell a friend you'll be 47 mins late etc. In the other it might be here in 2 mins or 20. You can't even nip to the loo without concern you might miss it. In the first instance you are mildly annoyed but you replan your time; in the second you're on edge for what turns out to only be 8mins, but a highly annoying and stressful 8 mins.

It's the uncertainty that's painful not so much the delay (for small-ish values of delay!)


Totally agree uncertainty is a problem - both when taking public transport and when using computer software - but in both cases it's surely better to primarily focus on ensuring operations aren't delayed unnecessarily, and only once you've got to a point that further optimisations become too difficult or expensive, turn to providing more accurate feedback on expected waiting times. I will say though accurately estimating waiting times (and esp. providing continual feedback on them) isn't always significantly less challenging than reducing them in the first place.


They make a common mistake in the "Frequency" section in regards to passive operations: The examples are a "frequent" operation (syncing your changes up) and a "rare" operation (remote changes being synced down while you are working). Frequency of course is not the salient property here. The difference in UX is practical: The latter requires your explicit attention, while the former does not.


Does scrolling past toc and the first paragraph skip a whole screen for anyone else? Or is it yet another UI advice article that cannot scroll itself.


Yes. Imagine you're giving UX advice while unable to get scrolling right.


Yes, seems like on mobile the table of contents collapses after you scroll past it. That causes quite a bug jump.


Exactly. https://i.imgur.com/kJlilJB.mp4

A post about UX with a non-working UX.


Why is this almost a comically common stereotype?


That’s because most of such “UX” articles are written by junior designers looking for a job (writing to promote themselves) or working in an IT services agency (writing to promote the agency).

These individuals tend not to have a whit of formal HCI education or real-world practice.

When you have little theoretical grounding and real world experience, all you have is examples from apps you spot in the wild to learn from. You don’t know what makes something work, and you have never seen it work yourself.

You might learn the wrong thing: that if some popular or financially-successful app out there has implemented a particular design pattern, that it must be a “best practice”.

It’s kind of like how most “how-to” articles, YouTube tutorials, and Udemy courses in software dev are made by junior devs.


I've noticed an obnoxious new UI anti-pattern showing up more frequently as of late: a loading page that renders a fake UI made of nothing but light grey boxes. More often than not, it's not even the actual UI layout of the page, just an artful rendition of a blank UI. These sorts of things drive me nuts, especially when it's a page whose contents could easily have been served as a static HTML page instead of rendering this silly fake UI via javascript then using more js + requests to replace that garbage with the actual content I want.


Everything-in-html requires to program MVC actions. Those patterns are often ugly, full of gotchas, hard to learn for kiddos, and old school. Plus, when the user clicks somewhere, we gotta load the new data with REST anyway, so that makes two ways to load data and it’s prone to mistakes, twice longer to develop.

Rendering placeholders allows us to load everything via REST. The data takes the same path every time, so, fewer bugs, streamlined way of loading data, plus kiddos love the simplicity of writing REST resources in Java. Also, each visual component can be programmed in isolation, encapsulating its own concerns, rather than having to mix all data into the MVC action channel. And isolation/encapsulation is good for reliability and sharing the work across a team.

One solution is to hydrate the initial HTML with the result of REST calls, but it has drawbacks.

I know it won’t mop your sorrow. It may just help to know there is a reasonable reason for that. I also find it infuriating to look at a page that says: “3, 2, 1, Done! I’m loaded, that wasn’t so ling was it?— oh wait, gotta load my data… Theeeere you go I’m loaded! No wait, that thing as well…”


As a "kiddo" I don't have any problems with first sending a html view that is made interactive with the js added to it.

But then again I tend to avoid frameworks and thing software developers shouldn't pick completely inefficent patterns even if it is very convenient for them.


That is mentioned in the article under 'Skeleton loader screen'. It can be nice on some slower/flakey connections, or on pages where different elements may load in at different times. Quite often the skeleton is static, and is then 'hydrated' with JS.

I agree it seems to be used unnecessarily more and more now though.


I remember reading about the proposed idea back on A List Apart like 12 years ago and thinking it was an interesting idea yet surprisingly have only seen them adopted widely in the last ~5 years.


If you'd read the linked article you'd have the vocabulary to speak about these UIs you hate so much.


You know it’s beyond help when a thing people hate is a named UX approach.


For my customer's multi-step wizard [0], I tried to make inevitable waiting less boring by showing a random AI-related fact, such as:

In 2017, Saudi Arabia has given citizenship to the social humanoid robot Sophia. She became the first-ever robot to get citizenship of any country in the world.

[0] https://www.kadoa.com/playground


> For high-stakes operations that are done occasionally, you might consider making tasks seem longer, as if more important, than they actually are.

Hmmm, not sure this is the best approach. There is no reason to deceive your user.


On the other hand, we're in the multi gigahertz, multi gagabyte era, there's almost no excuse for loading times except bloated libraries and not giving a damn about performance.


On the section regarding loading items in a list, the article notes to be careful with infinite loading or a "load more" button for performance reasons, which is totally correct but would it not be standard practice to have some kind of recycler/virtualizer mechanism in place if the UI is likely to be handling such large quantities of items?


This was my thought as well. Virtualization can reduce performance impact, but I like their point for pagination, that it gives users a specific place to link or reference.


Thanks, nice reference/summary that is easy to forget when you're deep down in your component tree :)


I don't need to know how long it's going to take (because that's always made up) but at least tell me what's going on in the background so I have some indication of progress. Loading is often made up of a few sequential steps so tell me where you're up to.


Instead of trying to build fanciest loading animation, maybe we should use "Network client hints" and send less data to slow networks? Figuring out how to use browser cache correctly and serving data from edge closest to client are also good options.


The first animated example, loading things one row at a time, is bad because you might think there's only one item in the result list, before it loads the next one.


> Progress bar

> Hot tip: Use an ease-in animation to make the progress seem like it’s accelerating.

So annoying when websites try to deceive me with a quantified progress bar inversely inching towards completion even though I cut off Internet five minutes ago.


All progress bars lie https://austinhenley.com/blog/fixprogressbars.html

It's a challenging problem, because either attempts at a solution (being direct, or being indirect) causes a worse user experience due to expectations of linear progress. For example proposed solution 1 in the article would have you create some sort of animation or additional progress bar to let people know if something is actually stalled/dropped vs something just taking more time, you would still be lying to them, but it's for the benefit of user experience.


A progress bar measures progress. Estimated time left is not part of a progress bar.


Unless they measure time left till the end of the universe, then it should be a linear progress bar as well!


This could be confirmation bias. All the times the progress bar just quietly did it's job, gave you some indication of progress and loaded without issue you are not likely to remember.

But the moment it's "wrong" it's annoying.


So what's a good programming design pattern to build good progress bars?

Progress bars for I/O are typically easy. Progress bars for computations with deep call graphs are typically more difficult.


I would say, don't use progress unless they are communicating useful and/or meaningful information?


Well, I bet a lot more would be possible with the right design patterns.


> For high-stakes operations that are done occasionally, you might consider making tasks seem longer, as if more important, than they actually are.

No. Just no.


Like all things, know your audience.

I know of a case where a delay was introduced because users didn’t believe the task was done because it happened so quickly, and contacted support thinking there was a problem (there wasn’t).

All related support contacts stopped after the artificial delay was introduced.

As you may have guessed, these were not technically savvy users. But it’s what they needed.


There are UX solutions that are not deceptive/time-wasting (e.g. a brief checkmark, but the actual solution will depend on many variables of the exact scenario).


How about dropping CPU clock speed until the computation actually takes avg 5-10 seconds?


This can happen to technically savvy users as well. For instance, when hard disks were only just becoming common, people were used to saving things to floppy disk, which was slow and loud. Hitting save typically meant the whole machine pausing for several seconds while you heard mechanical noises. When using hard drives for the first time, it wasn’t uncommon for people to keep hitting save repeatedly because they suddenly stopped getting the feedback they associated with saving. A similar effect occurs when moving from dial-up to broadband; and from whole page refreshes to Ajax.


In a way, this is kind of an indictment of modern software overall. Why doesn't the average person think that tasks can complete quickly? Because so much of the software they interact with is bloated, slow, terrible crap that they all think that's what is normal.

But on a more constructive note, perhaps instead of introducing artificial delays, you could instead show the user proof that the action has been completed?


Ah yes, I love my "wipe my hard drive and send my wife a text message that divorce papers are on the kitchen counter"-button next to my enter button on my keyboard!

Edit: Humans make mistakes, give them a little time for introspection before something non-reversible happens. Also, for people like you, there's usually an option for "don't show this again".


The feature you are looking for is the "undo" option. Gmail has it, many other apps have it, so you have time to cancel your divorce email.

Pretending to be busy for five seconds with no way to undo it or cancel the request while waiting won't save you your marriage.


There's a difference between "are you sure?" and the time.sleep(10) with a loading bar to make it seem like you're doing work that was done in the first 10 milliseconds.


During testing of a pension calculator I built, the users expressed more trust in the page when it took about 5-10 seconds to show results than when it showed them instantly. They equated instant results with a less complex model.


I've always considered this a good example of the rare case where metrics are true and meaningful (how the users feel), but should still be ignored in favor of principles (well, not ignore - one should look into other avenues to improve the user's perception of the tool). A more black-and-white example would be dark patterns that demonstrably increase user retention.


The unsend-within-a-minute feature in various email services/client is an example of this that is super useful.


That's a grace period - the parent is referring to faux loading times, e.g. a spinner that blocks the UI despite nothing happening.


I absolutely HATE the loading spinner. Even if a dev has no idea how long something is going to take, at least attach a number that increments in response to a listener event indicating some additional "tick" of work was done - otherwise there is zero visual difference between a process that is hung and one that is simply slow. It boggles my mind how many UI/UX people don't understand this.


It is one of the hardest things to get right in programming. and sometimes, like when you are waiting on a network and your connection is unreliable, just impossible.

It is hard because the blocking operation may be very deep in the call stack, and each element must collaborate. Not all APIs support asynchronous progress events. Proper progress bars are abstraction breakers. For example, your API may abstract away the fact you are on a network or doing things locally, except that if you want progress, these are very different cases, so you want to know the underlying situation. Ideally you should also know how good your connection is: an overcrowded airport Wi-Fi doesn't act the same as wired LAN.

UI/UX people can only do with what their back-end provides. The back-end can only do with what the APIs they use provide. The APIs can only do with what the system provides. So sometimes, a spinner is the only thing that can be done within a reasonable budget.


Is it so trivial to know if "some additional tick of work was done" without knowing how long something is going to take?


you are right -- it is not trivial, and heavily dependent on your use case. on my own scripts that run some kind of process on a CSV or Parquet file of a great deal of rows -- whose length I know, or can guess approximately based on file size and row width -- I passed a number and a function argument that would trigger after every so many rows. by default it would just print i/N every sqrt(N) times. this was ideal for my use case to keep an eye on it running in a tmux session.


Adding more complex loading states than spinners takes work and it’s not free.


Make your site fast enough, you don't need loading.


And what if your website does contain 'heavy' content like images, fonts, video, audio, and/or interactivity because you want or need it to?


> Is the loading user-triggered?

If not, then maybe just don't include it at all... I hate it when websites or apps do stuff spontaneously.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: