4.8 KiB
4.8 KiB
name, description, allowed-tools, version
| name | description | allowed-tools | version |
|---|---|---|---|
| composing-components | 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. | Read, Write, Edit | 1.0.0 |
Component Composition Patterns
This skill teaches you how to compose components effectively using React 19 patterns. 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
- Children Prop - Simplest composition, pass components as children
- Compound Components - Components that work together (Context-based)
- Render Props - Functions as children for flexibility
- 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
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
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:
<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
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} />)}
/>;
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:
<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.
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.)