Compound Components: What, Why and When.
From the archive · written in 2020
What are Compound Components? #
It’s a base component that expects to be combined with other component parts. Below the <Multiselect /> is the base component and the <Option /> and <Group /> components are the composable parts.
<Multiselect
selectedValues={[1, 2]}
onSelectedValuesChange={newValues => /* ... */}/>
<Option optionValue={1}>One</Option>
<Group header="The best numbers">
<Option optionValue={2}>Two</Option>
<Option optionValue={3}>Three</Option>
</Group>
</Multiselect>
It differs from the use of { children } props where you are not expecting any components in particular.
<Box>
<Header title="My Work" />
<Content>Lots of good stuff</Context>
</Box>
While the <Multiselect /> children will accept any components, having a combination of a base and a part component like <Option /> that is intended to be used with it passes our Compound Component definition.
Why would you opt for this pattern? #
The main advantage that the Compound Component pattern version offers is its flexibility. Want to make a <Multiselect /> of people that includes their avatar? Easy.
<Multiselect
selectedValues={[1, 2]}
onSelectedValuesChange={newValues => /* ... */}/>
<PersonOption personId={1}>Jane</Option>
<PersonOption personId={2}>John</Option>
</Multiselect>
We can support many variations without having to change the base components code.
To mirror the same behaviour using strict props we need to start adding flags like isPersonOptions:
<Multiselect
selectedValues={[1, 2]}
onSelectedValuesChange={newValues => /* ... */}
isPersonOptions={true}
options={[
{name: 'Jane', id: 1},
{name: 'John', id: 2},
]}/>
</Multiselect>
More flags will build up over time and the complexity will grow.
When should you use it? #
It’s when you need flexibility. Are you building something that will be widely used?
See React Component API Design for API design alternatives.
Thanks for reading.
More from the archive