New to Claude Skills? Learn how to install them →

davila7 on GitHub

React UI Patterns

Free

Implement modern UI patterns for React applications.

Get this skill

Free · Opens the source repo

What React UI Patterns does

React UI Patterns provides a comprehensive guide to implementing effective user interface patterns specifically for loading states, error handling, and data fetching in React applications. This skill is designed for developers who want to enhance the user experience by ensuring that their applications handle asynchronous data gracefully and informatively. By following the principles outlined in this skill, developers can avoid common pitfalls associated with stale UI, silent errors, and inadequate user feedback.

The skill emphasizes key concepts such as never showing stale UI, always surfacing errors, and employing optimistic updates to make the interface feel responsive. It includes practical examples that illustrate how to manage loading states effectively, ensuring that loading indicators are only displayed when no data is available. This approach helps to create a smooth user experience without unnecessary distractions.

In addition to loading states, React UI Patterns covers error handling strategies that prioritize user awareness. Developers are encouraged to surface errors clearly and provide options for users to retry actions when recoverable errors occur. The skill also addresses the importance of empty states, which should be incorporated into every list or collection to guide users effectively when no data is present.

Overall, this skill is a valuable resource for React developers looking to build robust applications that communicate effectively with users during data fetching and error conditions. By adhering to the guidelines provided, developers can create intuitive and user-friendly interfaces that enhance the overall application experience.

When to use it

Use this skill when building React components that require effective management of asynchronous data and user feedback.

When not to use it

This skill may not be suitable for applications that do not involve asynchronous data fetching or for developers who prefer to implement their own UI patterns without guidance.

What you can build with it

Loading Data in a Component

Implement the loading state pattern to show a spinner only when no data is available, improving user feedback during data fetching.

Handling Form Submissions

Utilize error handling patterns to surface validation errors and submission failures, ensuring users are informed of any issues.

Creating Empty States

Design contextual empty states for lists to guide users when no items are present, enhancing usability.

How to install React UI Patterns

View source

1. Install with the skills CLI

npx skills add davila7/claude-code-templates/react-ui-patterns --agent claude-code

2. 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 davila7

React UI Patterns

Core Principles

  1. Never show stale UI - Loading spinners only when actually loading
  2. Always surface errors - Users must know when something fails
  3. Optimistic updates - Make the UI feel instant
  4. Progressive disclosure - Show content as it becomes available
  5. 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 WhenUse Spinner When
Known content shapeUnknown content shape
List/card layoutsModal actions
Initial page loadButton submissions
Content placeholdersInline 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

Frequently asked questions about React UI Patterns

Similar skills