Files
gh-djankies-claude-configs-…/skills/composing-components/SKILL.md
2025-11-29 18:22:28 +08:00

214 lines
4.8 KiB
Markdown

---
name: composing-components
description: Teaches component composition patterns in React 19 including children prop, compound components, and render props. Use when designing component APIs, creating reusable components, or avoiding prop drilling.
allowed-tools: Read, Write, Edit
version: 1.0.0
---
# Component Composition Patterns
<role>
This skill teaches you how to compose components effectively using React 19 patterns.
</role>
<when-to-activate>
This skill activates when:
- Designing reusable component APIs
- Need to avoid prop drilling
- Creating compound components (Tabs, Accordion, etc.)
- Building flexible, composable interfaces
- Choosing between composition patterns
</when-to-activate>
<overview>
React 19 supports multiple composition patterns:
1. **Children Prop** - Simplest composition, pass components as children
2. **Compound Components** - Components that work together (Context-based)
3. **Render Props** - Functions as children for flexibility
4. **Composition over Props** - Prefer slots over configuration props
**When to Use:**
- Children prop: Simple containment
- Compound components: Coordinated behavior (tabs, accordions)
- Render props: Custom rendering logic
- Slots: Multiple insertion points
</overview>
<workflow>
## Pattern 1: Children Prop
```javascript
function Card({ children, header, footer }) {
return (
<div className="card">
{header && <div className="header">{header}</div>}
<div className="body">{children}</div>
{footer && <div className="footer">{footer}</div>}
</div>
);
}
<Card header={<h2>Title</h2>} footer={<button>Action</button>}>
<p>Card content goes here</p>
</Card>;
```
## Pattern 2: Compound Components
```javascript
import { createContext, use } from 'react';
const TabsContext = createContext(null);
export function Tabs({ children, defaultTab }) {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<TabsContext value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext>
);
}
export function TabList({ children }) {
return <div className="tab-list">{children}</div>;
}
export function Tab({ id, children }) {
const { activeTab, setActiveTab } = use(TabsContext);
return (
<button className={activeTab === id ? 'active' : ''} onClick={() => setActiveTab(id)}>
{children}
</button>
);
}
export function TabPanel({ id, children }) {
const { activeTab } = use(TabsContext);
if (activeTab !== id) return null;
return <div className="tab-panel">{children}</div>;
}
```
Usage:
```javascript
<Tabs defaultTab="profile">
<TabList>
<Tab id="profile">Profile</Tab>
<Tab id="settings">Settings</Tab>
</TabList>
<TabPanel id="profile">
<Profile />
</TabPanel>
<TabPanel id="settings">
<Settings />
</TabPanel>
</Tabs>
```
## Pattern 3: Render Props
```javascript
function DataProvider({ render, endpoint }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(endpoint)
.then((res) => res.json())
.then(setData);
}, [endpoint]);
return render({ data, loading: !data });
}
<DataProvider
endpoint="/api/users"
render={({ data, loading }) => (loading ? <Spinner /> : <UserList users={data} />)}
/>;
```
</workflow>
<examples>
## Example: Modal with Composition
```javascript
function Modal({ children, isOpen, onClose }) {
if (!isOpen) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal" onClick={(e) => e.stopPropagation()}>
{children}
</div>
</div>
);
}
function ModalHeader({ children }) {
return <div className="modal-header">{children}</div>;
}
function ModalBody({ children }) {
return <div className="modal-body">{children}</div>;
}
function ModalFooter({ children }) {
return <div className="modal-footer">{children}</div>;
}
Modal.Header = ModalHeader;
Modal.Body = ModalBody;
Modal.Footer = ModalFooter;
```
Usage:
```javascript
<Modal isOpen={showModal} onClose={() => setShowModal(false)}>
<Modal.Header>
<h2>Confirm Action</h2>
</Modal.Header>
<Modal.Body>
<p>Are you sure you want to proceed?</p>
</Modal.Body>
<Modal.Footer>
<button onClick={() => setShowModal(false)}>Cancel</button>
<button onClick={handleConfirm}>Confirm</button>
</Modal.Footer>
</Modal>
```
For comprehensive composition patterns, see: `research/react-19-comprehensive.md` lines 1263-1293.
</examples>
<constraints>
## MUST
- Use `use()` API for Context in React 19 (can be conditional)
- Keep compound components cohesive
- Document required child components
## SHOULD
- Prefer composition over complex prop APIs
- Use Context for compound component state
- Provide good component names for debugging
## NEVER
- Over-engineer simple components
- Use Context for non-coordinated components
- Forget to handle edge cases (missing children, etc.)
</constraints>