Compound Components
Compound components work like a family — they share state and work together to create a flexible, reusable API. Instead of one monolithic component doing everything, you get small pieces that compose together.
Think of HTML select and option tags. The parent manages the state, and children plug into it. That is the compound component pattern.
Building a Dropdown
Here is how you build a dropdown using compound components. The parent manages open/close state, and children handle their own rendering.
import { createContext, useContext, useState } from 'react'
const DropdownContext = createContext()
function Dropdown({ children }) {
const [open, setOpen] = useState(false)
return (
{children}
)
}
function DropdownTrigger({ children }) {
const { setOpen } = useContext(DropdownContext)
return (
)
}
function DropdownMenu({ children }) {
const { open } = useContext(DropdownContext)
if (!open) return null
return {children}
}
function DropdownItem({ onClick, children }) {
const { setOpen } = useContext(DropdownContext)
return (
{ onClick(); setOpen(false) }}>
{children}
)
}
Context connects the pieces. Each child reads or updates the shared state without prop drilling. Clean and elegant.
Using Your Compound Component
The usage feels natural and readable. You compose pieces together like building blocks.
function App() {
return (
Select an option
console.log('edit')}>
Edit
console.log('delete')}>
Delete
console.log('share')}>
Share
)
}
Look how clean that reads. The parent component does not know or care about the internal state management. It just composes the pieces it needs.
Real-World Examples
Many popular libraries use this pattern. React Router uses Route, Switch, and Link as compound components. Accordion components, tabs, and menus all benefit from this approach.
The key advantage is flexibility. Users can rearrange, omit, or extend parts without fighting the component. It gives power to the consumer while keeping the internals encapsulated.
Try it Yourself →