Compound Components is a design pattern in React where multiple components work together as a group, allowing the parent component to share implicit state with its children via React’s context or props.
The idea: The parent manages the state/behavior, and the children are flexible building blocks that know how to interact with the parent.
Sometimes, you want to give users more control over a component’s internal layout while still managing a shared state. Instead of passing a huge list of props down to one monolithic component, you split it into smaller related components.
Benefits:
- Flexible layout control for developers using your component
- Cleaner API — no long prop lists
- Children stay in sync through shared context
import React, { createContext, useContext, useState } from 'react';
const TabsContext = createContext();
function Tabs({ children, defaultIndex = 0 }) {
const [activeIndex, setActiveIndex] = useState(defaultIndex);
return (
<TabsContext.Provider value={{ activeIndex, setActiveIndex }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function TabList({ children }) {
return <div className="tab-list">{children}</div>;
}
function Tab({ index, children }) {
const { activeIndex, setActiveIndex } = useContext(TabsContext);
const isActive = activeIndex === index;
return (
<button
style={{
fontWeight: isActive ? 'bold' : 'normal',
borderBottom: isActive ? '2px solid blue' : 'none',
}}
onClick={() => setActiveIndex(index)}
>
{children}
</button>
);
}
function TabPanels({ children }) {
return <div className="tab-panels">{children}</div>;
}
function TabPanel({ index, children }) {
const { activeIndex } = useContext(TabsContext);
return activeIndex === index ? <div>{children}</div> : null;
}
// Assign as static properties (optional API style)
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panels = TabPanels;
Tabs.Panel = TabPanel;
export default Tabs;Usage:
<Tabs defaultIndex={0}>
<Tabs.List>
<Tabs.Tab index={0}>Home</Tabs.Tab>
<Tabs.Tab index={1}>Profile</Tabs.Tab>
</Tabs.List>
<Tabs.Panels>
<Tabs.Panel index={0}>Home Content</Tabs.Panel>
<Tabs.Panel index={1}>Profile Content</Tabs.Panel>
</Tabs.Panels>
</Tabs>- Parent manages state
- Children communicate via context
- Children don’t need prop drilling — they “just work” when placed inside the parent
✅ Pros
- Very flexible API for component consumers
- Avoids prop drilling
- Keeps components modular & reusable
- Slightly more boilerplate to set up
- Requires context knowledge
- May be overkill for very small components
<select>and<option>HTML pattern- React Router (
<Routes>and<Route>) - UI libraries like Radix UI or Chakra UI use this heavily (e.g.,
<Menu>,<MenuItem>)
If they ask:
“Why use compound components?” You can answer: It gives consumers control over layout and structure without losing state synchronization. It avoids long prop lists and keeps components focused.
Compound components are multiple React components that work together, sharing implicit state through context, giving the parent control over behavior and the consumer control over layout.