Composition over Configuration
Date: 2026-08-16
Let consumers assemble small pieces rather than configure one large one. Configuration grows without limit because every new requirement is another prop; composition doesn’t, because the pieces recombine.
Composition means a component exposes structural slots — children, named regions, sub-components — that the consumer fills. Configuration means it exposes props describing what to render.
The distinction only matters over time, which is why the wrong one is so easy to choose.
How configuration fails
It starts reasonably:
<Card title="Hello" body="Some text" />
Then requirements arrive:
<Card
title="Hello"
body="Some text"
image="/a.jpg"
imagePosition="top"
badge="New"
badgeColour="green"
footerText="Read more"
footerHref="/x"
showDivider
titleAs="h3"
compact
/>
Each prop was individually reasonable. The eleventh is where you notice, and by then every one is public API you can’t remove — Component API Design.
Worse, the next request is one the props can’t express: “two badges”, or “a link in the body”. There is no prop for that, so someone forks the component.
The composed version
<Card>
<Card.Image src="/a.jpg" />
<Card.Header>
<Card.Title as="h3">Hello</Card.Title>
<Badge tone="success">New</Badge>
</Card.Header>
<Card.Body>
Some text with a <a href="/y">link</a>.
</Card.Body>
<Card.Footer>
<Link href="/x">Read more</Link>
</Card.Footer>
</Card>More verbose, and it answers questions the props version couldn’t: two badges, arbitrary body content, a different footer element. None of those need a change to Card.
CONFIGURATION every new need
= a new prop
= permanent API
COMPOSITION every new need
= a new arrangement
= no API change
The trade
Composition is not free, and pretending otherwise is how it gets over-applied.
| Configuration | Composition | |
|---|---|---|
| Simple case | Shorter | More typing |
| Consistency | Enforced | Consumer’s job |
| New requirements | New prop | Rearrange |
| Discoverability | Autocomplete lists props | Needs docs |
| Wrong usage | Hard | Easy |
The real cost is consistency. If any arrangement is possible, inconsistent arrangements will appear. Configuration constrains by construction; composition constrains by documentation and review.
Choosing
- Configure what must not vary. A
varianton a button should be an enumerated prop — you actively want to prevent arbitrary buttons - Compose what legitimately varies. Content, arrangement, and anything you can’t enumerate
- Watch the prop count. Past roughly five or six props describing content, you are building a layout engine badly
- Watch for boolean pairs.
showHeaderplusheaderTextplusheaderAlignis three props doing one slot’s job
The signal to switch: you’re about to add a prop whose value is markup, or whose name is custom anything.
Provide both
The mature answer is usually a composed core with a convenient wrapper:
// the common case, configured
<Card title="Hello" body="Some text" />
// the same thing, composed, for when
// the common case isn't enough
<Card>
<Card.Title>Hello</Card.Title>
<Card.Body>Some text</Card.Body>
</Card>The wrapper is built from the composed parts, so there’s one implementation. Consumers reach for the short form and drop to the long one when they need to — no fork, no escape hatch, no prop explosion.
This is the pattern most mature libraries converge on, and it’s worth arriving at deliberately rather than after the eleventh prop — Headless Components, Polymorphic Components.