React UI Patterns
1. Never show stale UI - Loading spinners only when actually loading
What Is This
The React UI Patterns skill provides a set of best practices and patterns for managing UI states in modern React applications, focusing on how to handle loading states, errors, and data fetching in a user-centric way. These patterns help ensure that your React components always display the most accurate and helpful interface to users, especially in the presence of asynchronous data operations.
This skill is particularly relevant when building UI components that interact with remote data sources, handle asynchronous operations, or need to gracefully manage transitions between different states such as loading, error, and empty data sets. The patterns covered include rules for when to show loading spinners, how to surface errors, strategies for optimistic UI updates, progressive disclosure of data, and graceful degradation when only partial data is available.
Why Use It
React applications often fetch data asynchronously from APIs, which introduces a variety of states: loading, loaded, error, or empty. Poorly managed UI states lead to confusing user experiences. For example, showing loading spinners when data is already available can cause unnecessary flickers or hiding valuable cached information. Conversely, failing to surface errors leaves users in the dark, and abrupt UI changes can make an application feel unresponsive or unreliable.
The patterns provided by this skill ensure that:
- Loading indicators only appear when absolutely necessary, avoiding stale or flickering UI.
- Errors are always shown clearly, giving users a chance to understand and recover from failures.
- Optimistic updates provide instant feedback, making the UI feel fast and responsive.
- Data is progressively revealed, improving perceived performance.
- Partial data is handled gracefully, ensuring some content is always better than none.
Following these principles results in a smoother, more reliable, and user-friendly experience.
How to Use It
1. Never Show Stale
UI
Only display loading indicators when there is no data to show. If cached or previously loaded data exists, display it immediately, even if a background refetch is happening.
Correct Approach:
const { data, loading, error, refetch } = useGetItemsQuery();
if (error) return <ErrorState error={error} onRetry={refetch} />;
if (loading && !data) return <LoadingState />;
if (!data?.items.length) return <EmptyState />;
return <ItemList items={data.items} />;Incorrect Approach:
if (loading) return <LoadingState />; // This causes spinner flashes even with cached data2. Always Surface
Errors
Never hide failures. Always present error messages or states that inform the user what went wrong and provide a way to retry.
if (error) return <ErrorState error={error} onRetry={refetch} />;3. Optimistic
Updates
Update the UI instantly based on user actions, even before the server confirms the change. Optimistic updates make the app feel snappy, but you must handle rollbacks if the server returns an error.
function handleAddItem(newItem) {
// Optimistically update state
setItems((items) => [...items, newItem]);
// Send request
api.addItem(newItem).catch(() => {
// Rollback on error
setItems((items) => items.filter(item => item.id !== newItem.id));
});
}4. Progressive
Disclosure
Show parts of the UI as soon as their data becomes available, rather than waiting for all data to load. This often involves breaking up large data fetches or using skeleton screens for sections.
return (
<>
<ProfileSection user={user} />
{postsLoading ? <PostsSkeleton /> : <PostsList posts={posts} />}
</>
);5. Graceful
Degradation
If only partial data is available, show as much as possible rather than hiding everything. This helps users make progress even in degraded network conditions.
if (user && !posts) {
return (
<>
<ProfileSection user={user} />
<LoadingState message="Loading posts..." />
</>
);
}Decision Tree for Loading States
A useful mental model for handling loading states is:
- Is there an error?
- Yes: Show error state with retry
- No: Continue
- Is it loading and we have no data?
- Yes: Show loading indicator
- No: Continue
- Do we have data?
- Yes, with items: Show the data
- Yes, but empty: Show empty state
- No: Show loading fallback
When to Use It
Use these React UI patterns whenever you build components that:
- Fetch data asynchronously (REST, GraphQL, etc.)
- Need to handle multiple states (loading, error, empty, success)
- Display lists, dashboards, or detail views that should update in real time or upon user actions
- Want to provide a polished, user-friendly experience in the face of network variability
These patterns are especially valuable in applications where perceived performance and reliability are critical, such as dashboards, data-heavy UIs, or any client application where users expect up-to-date information.
Important Notes
- Always prefer showing cached or previously fetched data over a loading spinner.
- Avoid hiding errors from users - always provide feedback and a retry option.
- Use optimistic updates carefully, and make sure to handle rollbacks for failed requests.
- Progressive disclosure and graceful degradation both improve the perceived speed and reliability of your app.
- Adhering to these patterns leads to more maintainable and predictable UI components across your codebase.
By following these React UI patterns, you create user interfaces that are responsive, resilient, and clear, resulting in happier users and easier-to-maintain code.
More Skills You Might Like
Explore similar skills to enhance your workflow
Create Prd
Create a Product Requirements Document using a comprehensive 8-section template covering problem, objectives, segments, value propositions,
Go Concurrency Patterns
Production patterns for Go concurrency including goroutines, channels, synchronization primitives, and context management
Phase 1: Identify Balance Domain
If no argument, ask the user which system to check
.NET Backend Development Patterns
Master C#/.NET patterns for building production-grade APIs, MCP servers, and enterprise backends with modern best practices (2024/2025)
iOS Mobile Design
Master iOS Human Interface Guidelines (HIG) and SwiftUI patterns to build polished, native iOS applications that feel at home on Apple platforms
Automate Whatsapp
Build WhatsApp automations with Kapso workflows: configure WhatsApp triggers, edit workflow graphs, manage executions, deploy functions, and use