Compound Components
Compound components are a pattern where a parent component shares implicit state with its children, working together as a cohesive unit. Think <select> and <option> — they're meaningless apart but powerful together.
Compound components are a band — the lead singer (parent) sets the tempo, and each musician (child) plays their part by listening to the same rhythm (context). They're meaningless as solos.
Implementation uses Context: the parent provides state and callbacks, children consume them. Example: <Tabs> provides activeIndex and setActiveIndex, <Tab> reads activeIndex to style itself, <TabPanel> renders content based on activeIndex. The consumer uses it declaratively: <Tabs><Tab>A</Tab><Tab>B</Tab><TabPanel>Content A</TabPanel></Tabs>. This gives the user full control over rendering order and markup while the parent manages state.
Advanced compound components use React.Children.map or the Context pattern to support flexible nesting (children can be wrapped in divs or fragments). The Context approach is more robust because it doesn't rely on direct parent-child relationships. Libraries like Radix UI and Headless UI use this pattern extensively. Type safety can be achieved by constraining the context type and exposing only the compound component's public API through the parent's static properties: Tabs.Tab, Tabs.Panel.
Compound components let a parent and its children share implicit state via Context, working as a cohesive unit like HTML's select/option. The parent manages state, children consume it, and the user gets a declarative API with full control over rendering. I prefer the Context-based approach over React.Children manipulation for flexibility with nested structures.
Using React.Children.map breaks when children are wrapped in fragments or intermediate components. The Context-based approach avoids this fragility.