
React UI Patterns
FreeImplement modern loading and error handling in React.
Free · Opens the source repo
What React UI Patterns does
The React UI Patterns skill provides a comprehensive guide for implementing effective loading states, error handling, and data fetching patterns in React applications. It emphasizes the importance of user experience by ensuring that stale UI is avoided, errors are clearly communicated, and data is presented progressively. This skill is particularly beneficial for developers looking to enhance their UI components and manage asynchronous data more effectively.
This skill outlines core principles such as never showing stale UI, always surfacing errors, and employing optimistic updates to create a responsive user experience. It includes practical examples and decision trees to help developers determine the appropriate UI state to display based on the current data and loading conditions. By following these patterns, developers can ensure that their applications remain user-friendly and informative, even when data is being fetched or when errors occur.
The skill also covers specific patterns for handling loading states, error states, and empty states, providing clear guidance on how to implement each effectively. For instance, it advises showing loading indicators only when no data is available and ensuring that error messages are always visible to users. Additionally, it provides best practices for managing button states during asynchronous operations, ensuring that users cannot trigger multiple submissions inadvertently.
Overall, this skill is designed for React developers who want to build robust UI components that handle various states gracefully, improving both the functionality and user experience of their applications.
When to use it
Use this skill when developing React applications that require effective management of loading states, error handling, and data fetching.
When not to use it
This skill may not be suitable for applications that do not involve asynchronous data operations or where UI state management is not a concern.
What you can build with it
Building a Data-Driven Dashboard
Use this skill to implement loading states and error handling in a dashboard that fetches data from an API.
Creating a Form with Async Submission
Apply the patterns in this skill to manage form submission states, ensuring users receive feedback during the process.
Enhancing User Experience in E-commerce
Utilize the loading and error handling patterns to improve the user experience when displaying product data.
How to install React UI Patterns
View source1. Install with the skills CLI
npx skills add sickn33/agentic-awesome-skills/code-showcase-react-ui-patterns --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by sickn33React UI Patterns
When to Use
Use this skill when you need modern React UI patterns for loading states, error handling, and data fetching. Use when building UI components, handling async data, or managing UI states.
Core Principles
- Never show stale UI - Loading spinners only when actually loading
- Always surface errors - Users must know when something fails
- Optimistic updates - Make the UI feel instant
- Progressive disclosure - Show content as it becomes available
- Graceful degradation - Partial data is better than no data
Loading State Patterns
The Golden Rule
Show loading indicator ONLY when there's no data to display.
// CORRECT - Only show loading when no data exists
const { data, loading, error } = 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} />;
// WRONG - Shows spinner even when we have cached data
if (loading) return <LoadingState />; // Flashes on refetch!
Loading State Decision Tree
Is there an error?
→ Yes: Show error state with retry option
→ No: Continue
Is it loading AND we have no data?
→ Yes: Show loading indicator (spinner/skeleton)
→ No: Continue
Do we have data?
→ Yes, with items: Show the data
→ Yes, but empty: Show empty state
→ No: Show loading (fallback)
Skeleton vs Spinner
| Use Skeleton When | Use Spinner When |
|---|---|
| Known content shape | Unknown content shape |
| List/card layouts | Modal actions |
| Initial page load | Button submissions |
| Content placeholders | Inline operations |
Error Handling Patterns
The Error Handling Hierarchy
1. Inline error (field-level) → Form validation errors
2. Toast notification → Recoverable errors, user can retry
3. Error banner → Page-level errors, data still partially usable
4. Full error screen → Unrecoverable, needs user action
Always Show Errors
CRITICAL: Never swallow errors silently.
// CORRECT - Error always surfaced to user
const [createItem, { loading }] = useCreateItemMutation({
onCompleted: () => {
toast.success({ title: 'Item created' });
},
onError: (error) => {
console.error('createItem failed:', error);
toast.error({ title: 'Failed to create item' });
},
});
// WRONG - Error silently caught, user has no idea
const [createItem] = useCreateItemMutation({
onError: (error) => {
console.error(error); // User sees nothing!
},
});
Error State Component Pattern
interface ErrorStateProps {
error: Error;
onRetry?: () => void;
title?: string;
}
const ErrorState = ({ error, onRetry, title }: ErrorStateProps) => (
<div className="error-state">
<Icon name="exclamation-circle" />
<h3>{title ?? 'Something went wrong'}</h3>
<p>{error.message}</p>
{onRetry && (
<Button onClick={onRetry}>Try Again</Button>
)}
</div>
);
Button State Patterns
Button Loading State
<Button
onClick={handleSubmit}
isLoading={isSubmitting}
disabled={!isValid || isSubmitting}
>
Submit
</Button>
Disable During Operations
CRITICAL: Always disable triggers during async operations.
// CORRECT - Button disabled while loading
<Button
disabled={isSubmitting}
isLoading={isSubmitting}
onClick={handleSubmit}
>
Submit
</Button>
// WRONG - User can tap multiple times
<Button onClick={handleSubmit}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</Button>
Empty States
Empty State Requirements
Every list/collection MUST have an empty state:
// WRONG - No empty state
return <FlatList data={items} />;
// CORRECT - Explicit empty state
return (
<FlatList
data={items}
ListEmptyComponent={<EmptyState />}
/>
);
Contextual Empty States
// Search with no results
<EmptyState
icon="search"
title="No results found"
description="Try different search terms"
/>
// List with no items yet
<EmptyState
icon="plus-circle"
title="No items yet"
description="Create your first item"
action={{ label: 'Create Item', onClick: handleCreate }}
/>
Form Submission Pattern
const MyForm = () => {
const [submit, { loading }] = useSubmitMutation({
onCompleted: handleSuccess,
onError: handleError,
});
const handleSubmit = async () => {
if (!isValid) {
toast.error({ title: 'Please fix errors' });
return;
}
await submit({ variables: { input: values } });
};
return (
<form>
<Input
value={values.name}
onChange={handleChange('name')}
error={touched.name ? errors.name : undefined}
/>
<Button
type="submit"
onClick={handleSubmit}
disabled={!isValid || loading}
isLoading={loading}
>
Submit
</Button>
</form>
);
};
Anti-Patterns
Loading States
// WRONG - Spinner when data exists (causes flash)
if (loading) return <Spinner />;
// CORRECT - Only show loading without data
if (loading && !data) return <Spinner />;
Error Handling
// WRONG - Error swallowed
try {
await mutation();
} catch (e) {
console.log(e); // User has no idea!
}
// CORRECT - Error surfaced
onError: (error) => {
console.error('operation failed:', error);
toast.error({ title: 'Operation failed' });
}
Button States
// WRONG - Button not disabled during submission
<Button onClick={submit}>Submit</Button>
// CORRECT - Disabled and shows loading
<Button onClick={submit} disabled={loading} isLoading={loading}>
Submit
</Button>
Checklist
Before completing any UI component:
UI States:
- Error state handled and shown to user
- Loading state shown only when no data exists
- Empty state provided for collections
- Buttons disabled during async operations
- Buttons show loading indicator when appropriate
Data & Mutations:
- Mutations have onError handler
- All user actions have feedback (toast/visual)
Integration with Other Skills
- graphql-schema: Use mutation patterns with proper error handling
- testing-patterns: Test all UI states (loading, error, empty, success)
- formik-patterns: Apply form submission patterns
Limitations
- Use this skill only when the task clearly matches its upstream source and local project context.
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
Frequently asked questions about React UI Patterns
Similar skills
Playwright Component Testing
Test React and Vue components in isolation with Playwright.
Fluent UI Blazor
Integrate Fluent UI components in Blazor applications effortlessly.
Build MCP App
Create interactive UI widgets for MCP servers.
Web Design Reviewer
Identify and fix design issues in websites efficiently.
Markstream Install
Seamlessly integrate Markstream for Markdown rendering.
GSAP & Framer Scroll Animation
Create advanced scroll animations effortlessly.
