Loading states

What are loading states in UX?

Loading states are how an interface communicates that it's working while the user waits - a spinner, a skeleton screen, a determinate progress bar, or content streaming in. Good loading states reduce uncertainty, make a wait feel shorter, and reassure people the system hasn't frozen, turning dead time into a sense of progress.

Also known as: loading states, loaders, skeleton screens

Prefer to watch? Watch the recap 1:03

The demo

The same content takes the same time to load - but the way you show the wait changes everything. Try each loading state and notice which feels broken, which feels slow, and which feels quick.

What this demo shows (text version)

The same content fetch, taking the same amount of time, is shown with three different loading states. The blank state shows nothing while it loads, which makes people fear the app has frozen. The spinner shows the system is working but gives no sense of progress or what's coming. The skeleton screen shows a greyed-out preview of the incoming layout, so the structure appears immediately and the wait feels faster and calmer, even though the delay is identical.

That's the role of loading states: communicating that the system is working, reducing uncertainty, and making the wait feel shorter. Choose the right kind - determinate progress when you know the duration, skeletons for content about to appear, spinners only for short indefinite waits - and never leave people staring at nothing, wondering if it's broken.

How you show a wait matters as much as how long it is. A blank screen makes people fear the app has crashed; a clear loading state reassures them it's working and makes the wait feel shorter. Choose the right kind: a determinate progress bar when you know how long (it shows real progress), a skeleton screen for content that's about to appear (it previews the layout and feels faster than a spinner), and a spinner only for short, indefinite waits. Always give feedback within about a tenth of a second so an action feels acknowledged, and reserve full-screen blocking loaders for when you truly must. The goal is to never leave the user staring at nothing, wondering if it's broken.

The main loading patterns serve different needs. Spinners suit short, indefinite waits but convey no progress and feel slow if they linger. Skeleton screens show a greyed-out preview of the incoming layout, setting expectations and feeling faster because the structure appears immediately. Determinate progress bars suit longer, measurable operations, showing real advancement (and pairing with the labour illusion by naming steps). Streaming/partial loading shows content as it arrives so people can start reading sooner.

Loading states are perceived-performance tools grounded in time perception: occupied, explained, certain waits feel far shorter than blank, silent ones. Immediate acknowledgement matters too - responding within roughly 100 ms (the Doherty threshold territory) makes an action feel instant, so even before data arrives, a button should visibly register the press. The worst pattern is a frozen, feedback-free screen that looks broken.

Choose deliberately: determinate progress when duration is known; skeletons for content-heavy screens about to populate; spinners only for brief, unknown waits; optimistic UI to skip the wait entirely where safe. Avoid janky spinner flashes for sub-second loads, prevent layout shift when real content replaces placeholders, and keep blocking, full-screen loaders for genuinely unavoidable waits. Never show nothing.