Compound Components share implicit state between related components (like <select>/<option>). Render Props pass render functions to share code. Custom Hooks extract reusable stateful logic. Today, Custom Hooks are preferred over Render Props for most cases.
Compound Components:
Render Props:
Custom Hooks:
Pattern Evolution:
// Context for implicit state sharing
const TabsContext = createContext();
function Tabs({ children, defaultTab }) {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function TabList({ children }) {
return <div className="tab-list">{children}</div>;
}
function Tab({ id, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
return (
<button
className={activeTab === id ? 'active' : ''}
onClick={() => setActiveTab(id)}
>
{children}
</button>
);
}
function TabPanels({ children }) {
return <div className="tab-panels">{children}</div>;
}
function TabPanel({ id, children }) {
const { activeTab } = useContext(TabsContext);
if (activeTab !== id) return null;
return <div className="tab-panel">{children}</div>;
}
// Usage - clean, declarative API
function App() {
return (
<Tabs defaultTab="tab1">
<TabList>
<Tab id="tab1">First</Tab>
<Tab id="tab2">Second</Tab>
</TabList>
<TabPanels>
<TabPanel id="tab1">First content</TabPanel>
<TabPanel id="tab2">Second content</TabPanel>
</TabPanels>
</Tabs>
);
}