Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    neversight

    react-ui-patterns

    neversight/react-ui-patterns
    Design
    2
    1 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Modern React UI patterns for loading states, error handling, and data fetching. Use when building UI components, handling async data, or managing UI states.

    SKILL.md

    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 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
    Recommended Servers
    Svelte
    Svelte
    Vercel Grep
    Vercel Grep
    Browser tool
    Browser tool
    Repository
    neversight/skills_feed