Tags: web-dev concept

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.

ConfigurationComposition
Simple caseShorterMore typing
ConsistencyEnforcedConsumer’s job
New requirementsNew propRearrange
DiscoverabilityAutocomplete lists propsNeeds docs
Wrong usageHardEasy

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 variant on 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. showHeader plus headerText plus headerAlign is 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.