Claude-skill-registry component-composition

Component composition patterns with nesting, slots, compound components, and render props. Use when creating reusable components, component APIs, or complex component hierarchies.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/component-composition" ~/.claude/skills/majiayu000-claude-skill-registry-component-composition && rm -rf "$T"
manifest: skills/data/component-composition/SKILL.md
source content

Component Composition

Advanced composition patterns for scalable components.

Agent Workflow (MANDATORY)

Before implementation:

  1. fuse-ai-pilot:explore-codebase - Check existing composition patterns
  2. fuse-ai-pilot:research-expert - React 19 composition patterns

After: Run fuse-ai-pilot:sniper for validation.

Pattern Overview

PatternUse CaseComplexity
ChildrenSimple containersLow
SlotsNamed regionsMedium
CompoundRelated sub-componentsMedium
Render PropsCustom renderingHigh
ContextShared stateHigh

1. Children Pattern (Basic)

function Card({ children, className }: { children: React.ReactNode }) {
  return (
    <div className={cn("bg-surface rounded-2xl p-6", className)}>
      {children}
    </div>
  );
}

// Usage
<Card>
  <h2>Title</h2>
  <p>Content</p>
</Card>

2. Slots Pattern

interface CardProps {
  header?: React.ReactNode;
  footer?: React.ReactNode;
  children: React.ReactNode;
}

function Card({ header, footer, children }: CardProps) {
  return (
    <div className="bg-surface rounded-2xl overflow-hidden">
      {header && (
        <div className="px-6 py-4 border-b border-border">
          {header}
        </div>
      )}
      <div className="p-6">{children}</div>
      {footer && (
        <div className="px-6 py-4 border-t border-border bg-muted/50">
          {footer}
        </div>
      )}
    </div>
  );
}

// Usage
<Card
  header={<h2>Settings</h2>}
  footer={<Button>Save</Button>}
>
  <p>Card content here</p>
</Card>

3. Compound Components

const CardContext = createContext<{ variant: string }>({ variant: "default" });

function Card({ children, variant = "default" }) {
  return (
    <CardContext.Provider value={{ variant }}>
      <div className="bg-surface rounded-2xl">{children}</div>
    </CardContext.Provider>
  );
}

Card.Header = function CardHeader({ children }) {
  return <div className="px-6 py-4 border-b">{children}</div>;
};

Card.Body = function CardBody({ children }) {
  return <div className="p-6">{children}</div>;
};

Card.Footer = function CardFooter({ children }) {
  const { variant } = useContext(CardContext);
  return (
    <div className={cn("px-6 py-4", variant === "glass" && "bg-white/5")}>
      {children}
    </div>
  );
};

// Usage
<Card variant="glass">
  <Card.Header>
    <h2>Title</h2>
  </Card.Header>
  <Card.Body>
    <p>Content</p>
  </Card.Body>
  <Card.Footer>
    <Button>Action</Button>
  </Card.Footer>
</Card>

4. Render Props

interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  renderEmpty?: () => React.ReactNode;
}

function List<T>({ items, renderItem, renderEmpty }: ListProps<T>) {
  if (items.length === 0) {
    return renderEmpty?.() ?? <p>No items</p>;
  }

  return (
    <ul className="space-y-2">
      {items.map((item, i) => (
        <li key={i}>{renderItem(item, i)}</li>
      ))}
    </ul>
  );
}

// Usage
<List
  items={users}
  renderItem={(user) => <UserCard user={user} />}
  renderEmpty={() => <EmptyState message="No users found" />}
/>

5. As Prop (Polymorphic)

type ButtonProps<T extends React.ElementType> = {
  as?: T;
  children: React.ReactNode;
} & React.ComponentPropsWithoutRef<T>;

function Button<T extends React.ElementType = "button">({
  as,
  children,
  ...props
}: ButtonProps<T>) {
  const Component = as || "button";
  return (
    <Component
      className="px-4 py-2 bg-primary text-primary-foreground rounded-lg"
      {...props}
    >
      {children}
    </Component>
  );
}

// Usage
<Button>Click me</Button>
<Button as="a" href="/link">Link button</Button>
<Button as={Link} to="/route">Router link</Button>

6. Forwarded Refs

const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
  return (
    <input
      ref={ref}
      className="w-full px-4 py-2 rounded-lg border"
      {...props}
    />
  );
});
Input.displayName = "Input";

// Usage with ref
const inputRef = useRef<HTMLInputElement>(null);
<Input ref={inputRef} />

Composition Guidelines

DODON'T
Use children for simple nestingOver-engineer simple components
Use slots for named regionsUse too many slots (max 3-4)
Use compound for related partsCreate deep nesting (max 2 levels)
Forward refs for form elementsForget displayName

Validation

[ ] Appropriate pattern for complexity
[ ] TypeScript props properly typed
[ ] displayName set on forwardRef
[ ] Context used sparingly
[ ] Max 2-3 composition levels
[ ] Documented API in JSDoc

References

  • ../../references/design-patterns.md
    - Component patterns
  • ../../references/component-examples.md
    - Production examples
component-composition — OpenSkillIndex