Why in-between states matter and how to solve the coordination problem.
Have you ever used an app and thought "this feels slow" or "this is janky" without knowing exactly why? The answer often lies in the in-between moments—loading screens, error states, and the gaps between user action and final render.
Building async UIs has always been difficult. Navigation hides content behind spinners, search boxes create race conditions, and form submissions require manual state management for every loading flag. Every async operation forces you to orchestrate the coordination manually.
This isn't a performance problem—it's a coordination problem. And React's primitives now solve it declaratively.
Every async operation has three phases:
User Action → Loading → Success or Error
In React applications, this happens constantly:
The in-between states determine how polished your app feels:
| Bad UX | Good UX |
|---|---|
| Blank screens | Skeleton placeholders |
| Jumping layouts (CLS) | Stable, reserved space |
| Frozen buttons | Instant optimistic feedback |
| Full-page spinners | Localized loading indicators |
| Generic errors | Contextual error recovery |
These are UX problems, which is why engineers often overlook them. We focus on making things work, not on what users see while they wait.
The following posts explore each pattern with real examples from this app—showing how to coordinate loading, mutations, and navigation seamlessly.