Conditional Rendering Strategies
On this page
Why Conditional Rendering Fails in Production
- Most UIs have multiple states: loading, error, empty, ready, partial.
- Ad-hoc booleans create contradictions (e.g., loading + error + data).
- Inconsistent transitions cause flicker and confusing user feedback.
State Enumeration (Baseline)
- loading: request in-flight, no usable data.
- error: request failed, show actionable retry.
- empty: request succeeded but no results.
- ready: data is available and valid.
Preferred Pattern: Early Returns
function UsersView({ state }) {
if (state.status === 'loading') return <Spinner />;
if (state.status === 'error') return <ErrorBox message={state.message} />;
if (state.status === 'empty') return <EmptyState />;
return <UserTable users={state.users} />;
}
Avoid Boolean Soup
// ❌ Hard to reason about, easy to contradict if (isLoading) ... if (hasError) ... if (data) ... if (!data) ... // ✅ Use a single state machine-like status // status: loading | error | empty | ready
Operational Checklist
- Every async view has explicit loading, error, empty, ready UI.
- Error UI includes a retry action and preserves context.
- Loading UI is stable (no layout jumps if possible).
- Transitions are intentional (don't clear data unless required).
Failure Modes
- Flicker: clearing data on refetch causes empty → loading → ready flashes.
- Silent failure: error state hidden by a falsey condition.
- Impossible combinations: multiple booleans true at once.
- Retry loops: auto-retry without caps or user control.
Incident Triage Playbook
- Step 1: Confirm state model (is it enumerated or boolean soup?).
- Step 2: Verify transitions on refetch (keep previous data or not?).
- Step 3: Ensure error UI is reachable and actionable.
- Step 4: Add logging around state transitions to prove the sequence.
Code Review Checklist
- No nested ternary chains for major view states.
- Error and empty states are explicitly handled.
- State transitions are deterministic and testable.