State Flow (State Diagram)
A state flow maps every state an interface, component, or screen can be in, plus the events that move between them. Where a user flow asks "where does the person go next?", a state flow asks "what is the software BEING right now, and what can change that?" Near-synonyms: state diagram, state machine, state-transition diagram, statechart, "UI states." This file uses "state flow" for the design artifact and "state machine" for the formal model underneath it.
Formal lineage
State diagrams come from finite-state machines in computer science. David Harel's statecharts (1987) extended them with hierarchy (states inside states), concurrency (independent regions running at once), and communication — precisely the properties real UIs have: a "Checkout" superstate containing loading/error substates while a "Cart badge" region updates independently. Harel's formalism became the basis of UML state machines and, later, of frontend state-machine libraries (XState and peers — a practitioner convention, not a standard). You rarely need full statechart notation; you always need its discipline: finite named states, explicit events, no undefined combinations.
The designer's cut of the same idea is Scott Hurff's "UI Stack": every screen exists in five states — ideal, empty, error, partial, loading — and shipping only the ideal state is shipping a fifth of the screen.
When to use one
- Any component with async behavior: uploads, saves, payments, search, sync, anything that talks to a network.
- Any screen fed by data that can be absent, partial, stale, or denied.
- Design-system component specs (see below — this is mandatory there).
- Debugging "it sometimes shows the wrong thing": impossible-state bugs are almost always missing states or missing transitions.
What it answers
- What states exist? (Finite list — if you can't enumerate them, the component isn't specified.)
- What event causes each transition, and what does the user see during and after it?
- Which states are dead ends? (There should be none — every state needs at least one exit; cross-ref Error Flow.)
The working state inventory
Run every screen and interactive component through this list; strike what genuinely can't occur, design what remains:
- Idle / default — loaded, ready, nothing in flight.
- Loading — initial load, refresh, and in-action loads are three different designs (pattern evidence: Feedback, Loading, Errors & Recovery).
- Empty — no data yet, cleared, or no matches; routes in/out are mapped in Empty State Flow.
- Partial — some data present, less than ideal (1 of 50 items, a half-filled profile); often the most common real state and the least designed one.
- Error — failed, with cause and recovery (Error Flow).
- Success — completed; confirm, then route somewhere useful.
- Offline — no connection: what's cached, what's queued, what's blocked (Feedback, Loading, Errors & Recovery covers offline/sync patterns).
- Disabled — present but unavailable, with a discoverable reason.
- Permission-denied — visible but not allowed for this user or tier (routes and choices in Permission Flow).
- Retry — mid-recovery: attempt count, backoff, when to stop.
Example — Upload component
Idle ──select file──▶ Uploading ──upload done──▶ Processing ──ok──▶ Success
│ │
│ network drops │ processing fails
▼ ▼
Network Error Failed
│ ├─ Retry ─────▶ Uploading (attempt n+1,
│ │ keep the file — never
│ │ make the user re-pick it)
│ └─ Cancel ────▶ Idle
│ 3 retries exhausted │
▼ ▼
Give clear error Contact Support
+ keep file queued (with error reference
for when back online + the file preserved)
Note what the diagram forced into the open: retry keeps the file; retries are capped; Processing can fail after Uploading succeeded (two different errors with different fixes); Success and Failed both have exits. None of that appears in a static comp of the upload screen.
Why this is the design-system and handoff essential
A comp without states is half a spec. If the designer doesn't define loading, empty, error, and disabled, the developer will — at 6 p.m., without research, in whatever the framework defaults to. The design system file in this library makes this explicit: every component spec must document all states (default/hover/focus-visible/active/disabled/ loading/error — see Building Your Own Design System). The state flow is how you find those states before the spec is written, and the artifact you attach to the handoff so "what happens if the request fails?" is answered in the file, not in Slack.
Practical format: one state flow per component or screen, text-first (as above) so it lives in the repo and diffs with the code.
Common mistakes
- Designing only ideal + error, skipping partial and empty (the two states most users actually meet first).
- Implicit states: a boolean
isLoadingnext to a booleanhasErrorcreates four combinations; you designed two. Enumerate states, don't stack flags. - Transitions with no trigger ("it just goes back to normal") or states with no exit.
- Treating offline as an error instead of a state with its own rules.
Checklist
- Every screen/component has a finite, named list of states.
- All ten inventory states considered; exclusions are deliberate.
- Every transition names its trigger (user event, system event, timeout, or data change).
- No dead-end states: error, denied, and offline all have exits.
- User work survives every transition (file kept through retry, text kept through failure).
- Loading distinguishes first load vs. refresh vs. in-action.
- The flow ships with the handoff and updates when behavior does.
- Cross-checked against Error Flow, Empty State Flow, and Permission Flow for the states those files own.
Sources
- Harel, D. (1987). "Statecharts: a visual formalism for complex systems." Science of Computer Programming, 8(3), 231–274 — hierarchy, concurrency, communication in state diagrams.
- Object Management Group — UML 2.5.1 specification, state machines (omg.org/spec/UML) — the standardized descendant notation.
- Hurff, S. (2016). Designing Products People Love. O'Reilly, ch. 6 — the "UI Stack": ideal/empty/error/partial/loading; also scotthurff.com, "Why your user interface is awkward — you're ignoring the UI Stack."
- Nielsen Norman Group — visibility of system status, heuristic #1 (nngroup.com/articles/ten-usability-heuristics/).