Latency is a design material
The fastest interface isn’t the one with the lowest numbers in the network tab. It’s the one that never makes you wait for something you’ve already decided to do.
Those are different problems. The first is engineering’s. The second is ours, and we tend to hand it back.
Three ways to spend a round trip
When a user acts and the result depends on a server, you have roughly 100–400ms to fill. There are only three honest options:
- Show the outcome now, reconcile later. Optimistic UI. The edit applies locally on the frame it happens; the server confirms in the background. Best when the action almost always succeeds and a rare rollback is cheap to explain.
- Show that the system heard you. A state change on the control itself — the button depresses and holds, the row dims. Cheap, universally applicable, and it buys you the full budget before anyone feels ignored.
- Show the work. A determinate progress indication, used only when the wait is genuinely long and variable. A spinner is this option giving up.
Most janky interfaces are picking option 3 for a job that needed option 1 or 2.
The correction is part of the design
Optimistic UI gets dismissed as “lying to the user.” It’s only lying if you don’t design the moment you’re wrong. When a reconcile fails, the element should animate from where the user put it to where it actually belongs, over ~200ms, so the correction reads as a physical fact rather than a glitch. Done well, people barely register it. Done as an instant snap, it feels broken.
What to actually do
- Instrument perceived latency, not just server latency. Time from interaction to meaningful visual feedback.
- Set a budget per interaction type and hold the line in review.
- Prototype the laggy states on purpose. Inject 300ms and live in it for an afternoon.
Latency is going to be in your product whether you design for it or not. You may as well decide how it feels.