Claude
Skills
Sign in
Back

ui-component-patterns

Included with Lifetime
$97 forever

Build reusable, maintainable UI components following modern design patterns. Use when creating component libraries, implementing design systems, or building scalable frontend architectures. Handles React patterns, composition, prop design, TypeScript, and component best practices.

Design

What this skill does



# UI Component Patterns


## When to use this skill

- **Building Component Libraries**: Creating reusable UI components
- **Implementing Design Systems**: Applying consistent UI patterns
- **Complex UI**: Components requiring multiple variants (Button, Modal, Dropdown)
- **Refactoring**: Extracting duplicate code into components

## Instructions

### Step 1: Props API Design

Design Props that are easy to use and extensible.

**Principles**:
- Clear names
- Reasonable defaults
- Type definitions with TypeScript
- Optional Props use optional marker (?)

**Example** (Button):
```tsx
interface ButtonProps {
  // Required
  children: React.ReactNode;

  // Optional (with defaults)
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  isLoading?: boolean;

  // Event handlers
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;

  // HTML attribute inheritance
  type?: 'button' | 'submit' | 'reset';
  className?: string;
}

function Button({
  children,
  variant = 'primary',
  size = 'md',
  disabled = false,
  isLoading = false,
  onClick,
  type = 'button',
  className = '',
  ...rest
}: ButtonProps) {
  const baseClasses = 'btn';
  const variantClasses = `btn-${variant}`;
  const sizeClasses = `btn-${size}`;
  const classes = `${baseClasses} ${variantClasses} ${sizeClasses} ${className}`;

  return (
    <button
      type={type}
      className={classes}
      disabled={disabled || isLoading}
      onClick={onClick}
      {...rest}
    >
      {isLoading ? <Spinner /> : children}
    </button>
  );
}

// Usage example
<Button variant="primary" size="lg" onClick={() => alert('Clicked!')}>
  Click Me
</Button>
```

### Step 2: Composition Pattern

Combine small components to build complex UI.

**Example** (Card):
```tsx
// Card component (Container)
interface CardProps {
  children: React.ReactNode;
  className?: string;
}

function Card({ children, className = '' }: CardProps) {
  return <div className={`card ${className}`}>{children}</div>;
}

// Card.Header
function CardHeader({ children }: { children: React.ReactNode }) {
  return <div className="card-header">{children}</div>;
}

// Card.Body
function CardBody({ children }: { children: React.ReactNode }) {
  return <div className="card-body">{children}</div>;
}

// Card.Footer
function CardFooter({ children }: { children: React.ReactNode }) {
  return <div className="card-footer">{children}</div>;
}

// Compound Component pattern
Card.Header = CardHeader;
Card.Body = CardBody;
Card.Footer = CardFooter;

export default Card;

// Usage
import Card from './Card';

function ProductCard() {
  return (
    <Card>
      <Card.Header>
        <h3>Product Name</h3>
      </Card.Header>
      <Card.Body>
        <img src="..." alt="Product" />
        <p>Product description here...</p>
      </Card.Body>
      <Card.Footer>
        <button>Add to Cart</button>
      </Card.Footer>
    </Card>
  );
}
```

### Step 3: Render Props / Children as Function

A pattern for flexible customization.

**Example** (Dropdown):
```tsx
interface DropdownProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  onSelect: (item: T) => void;
  placeholder?: string;
}

function Dropdown<T>({ items, renderItem, onSelect, placeholder }: DropdownProps<T>) {
  const [isOpen, setIsOpen] = useState(false);
  const [selected, setSelected] = useState<T | null>(null);

  const handleSelect = (item: T) => {
    setSelected(item);
    onSelect(item);
    setIsOpen(false);
  };

  return (
    <div className="dropdown">
      <button onClick={() => setIsOpen(!isOpen)}>
        {selected ? renderItem(selected, -1) : placeholder || 'Select...'}
      </button>

      {isOpen && (
        <ul className="dropdown-menu">
          {items.map((item, index) => (
            <li key={index} onClick={() => handleSelect(item)}>
              {renderItem(item, index)}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

// Usage
interface User {
  id: string;
  name: string;
  avatar: string;
}

function UserDropdown() {
  const users: User[] = [...];

  return (
    <Dropdown
      items={users}
      placeholder="Select a user"
      renderItem={(user) => (
        <div className="user-item">
          <img src={user.avatar} alt={user.name} />
          <span>{user.name}</span>
        </div>
      )}
      onSelect={(user) => console.log('Selected:', user)}
    />
  );
}
```

### Step 4: Separating Logic with Custom Hooks

Separate UI from business logic.

**Example** (Modal):
```tsx
// hooks/useModal.ts
function useModal(initialOpen = false) {
  const [isOpen, setIsOpen] = useState(initialOpen);

  const open = useCallback(() => setIsOpen(true), []);
  const close = useCallback(() => setIsOpen(false), []);
  const toggle = useCallback(() => setIsOpen(prev => !prev), []);

  return { isOpen, open, close, toggle };
}

// components/Modal.tsx
interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  title: string;
  children: React.ReactNode;
}

function Modal({ isOpen, onClose, title, children }: ModalProps) {
  if (!isOpen) return null;

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        <div className="modal-header">
          <h2>{title}</h2>
          <button onClick={onClose} aria-label="Close">×</button>
        </div>
        <div className="modal-body">{children}</div>
      </div>
    </div>
  );
}

// Usage
function App() {
  const { isOpen, open, close } = useModal();

  return (
    <>
      <button onClick={open}>Open Modal</button>
      <Modal isOpen={isOpen} onClose={close} title="My Modal">
        <p>Modal content here...</p>
      </Modal>
    </>
  );
}
```

### Step 5: Performance Optimization

Prevent unnecessary re-renders.

**React.memo**:
```tsx
// ❌ Bad: child re-renders every time parent re-renders
function ExpensiveComponent({ data }) {
  console.log('Rendering...');
  return <div>{/* Complex UI */}</div>;
}

// ✅ Good: re-renders only when props change
const ExpensiveComponent = React.memo(({ data }) => {
  console.log('Rendering...');
  return <div>{/* Complex UI */}</div>;
});
```

**useMemo & useCallback**:
```tsx
function ProductList({ products, category }: { products: Product[]; category: string }) {
  // ✅ Memoize filtered results
  const filteredProducts = useMemo(() => {
    return products.filter(p => p.category === category);
  }, [products, category]);

  // ✅ Memoize callback
  const handleAddToCart = useCallback((productId: string) => {
    // Add to cart
    console.log('Adding:', productId);
  }, []);

  return (
    <div>
      {filteredProducts.map(product => (
        <ProductCard
          key={product.id}
          product={product}
          onAddToCart={handleAddToCart}
        />
      ))}
    </div>
  );
}

const ProductCard = React.memo(({ product, onAddToCart }) => {
  return (
    <div>
      <h3>{product.name}</h3>
      <button onClick={() => onAddToCart(product.id)}>Add to Cart</button>
    </div>
  );
});
```

## Output format

### Component File Structure

```
components/
├── Button/
│   ├── Button.tsx           # Main component
│   ├── Button.test.tsx      # Tests
│   ├── Button.stories.tsx   # Storybook
│   ├── Button.module.css    # Styles
│   └── index.ts             # Export
├── Card/
│   ├── Card.tsx
│   ├── CardHeader.tsx
│   ├── CardBody.tsx
│   ├── CardFooter.tsx
│   └── index.ts
└── Modal/
    ├── Modal.tsx
    ├── useModal.ts          # Custom hook
    └── index.ts
```

### Component Template

```tsx
import React from 'react';

export interface ComponentProps {
  // Props definition
  children: React.ReactNode;
  className?: string;
}

/**
 * Component description
 *
 * @example
 * ```tsx
 * <Component>Hello</Component>
 * ```
 */
export const Component = React.forwardRef<HTMLDivElement, ComponentProps>(
  ({ children, className = '', ...rest }, ref)

Related in Design