The Over-Configuration Trap: Why Multi-Prop Components Fail In short: Every boolean you add to a component doubles its state space. Five booleans is thirty-two layouts you have promised to support and will never test. The fix is not fewer features — it is moving content decisions to the consumer. Failed UI components all fail the same way. They start clean and elegant, and over six months of sprint planning they mutate into something nobody wants to touch. The mutation has one driver: a shared component needs to look slightly different for a new feature, and the instinct is to add a configuration property. How a god component is born You build a lovely simple card for the tanker intake app. Then the requests arrive, and every one of them is reasonable: Lab testing: "We need a pass/fail badge in the top right." → , Billing: "We need a footer with a Pay Invoice button." → , , Farmer portal: "The heading needs an icon, but only for premium suppliers." → , Dispatch: "Ours needs to be tighter, we show forty at once." → Lab again: "Can the badge go on the left sometimes?" → A year later: Nobody made a bad decision. Each addition was a small, justified yes. The three costs 1. The template becomes conditional logic The DOM structure now depends on boolean combinations. Grid and flex layouts assume a stable child order, and this one does not have one — so layouts break in combinations nobody rendered. 2. The states multiply past testing Six booleans and two enums on that card: 2⁶ × 3 × 2 = 384 distinct visual states. You will write maybe eight stories. The other 376 ship unverified, and each one is a layout somebody is allowed to ask for, because the type system says it is legal. 3. It becomes unchangeable Every input might be bound somewhere across twenty repositories. looks removable until you find three apps using it. Removing it is a deprecation cycle and a major version — for a property you regret. That is the compounding problem: inputs are permanent (Chapter 4), and configuration inputs arrive faster than any other kind. What went wrong, precisely is trying to micromanage what goes inside it. A layout component's job is the box: padding, border, elevation, the arrangement of header, body, and footer. It has no business knowing what a badge is, what an invoice button is, or what a premium supplier icon looks like. Every one of those requests was actually a request for space to put something , not for a feature. Six teams each wanted a slot, and got a configuration flag instead. The shape of the fix The second version has zero inputs on the card . And notice what else changed: is a shared , domain-aware component (Chapter 2, Tier 2), sitting inside a library component that has never heard of milk. The tiers stay clean because the card does not need to know. Now when lab testing wants two badges, they write two badges. No ticket, no release, no version bump. The next lesson builds this properly. When an input is still right Composition is not the answer to everything. The distinction is content versus behaviour: Input controls… Verdict Example --- --- --- What content appears Use a slot , , How the component behaves Keep the input , , A genuine visual variant of the box itself Keep it, as a union Data the component renders itself Keep it on a stepper is a real input: it is about the component's own behaviour, and every consumer means the same thing by it. is not: it is a slot with a boolean pretending to be a feature. And prefer a union to a set of booleans even for real variants. is three states. plus plus is eight, five of which are contradictory. "But I thought a component with more options was more reusable" This is the belief the whole chapter exists to dismantle, and it deserves a fair hearing, because it is not stupid — it is a correct intuition applied to the wrong axis. The intuition: a component that handles more cases gets used in more places. True. The error: assuming configuration is how you handle more cases. Configuration handles the cases you predicted . Composition handles the cases you did not. Watch the two diverge on a request nobody planned for. Lab testing needs a card whose header has a badge, a timestamp, and a warning icon that only appears when the sample is over 24 hours old. Configured card: not supported. There is but no , no conditional icon. File a ticket. The library team adds three inputs and one output, releases a minor version, and twenty teams get a slightly larger API they did not ask for. Elapsed time: a sprint. Composed card: the lab team writes the header they want, in their own template, in ten minutes. The library is untouched. Nobody else's API changed. The count that makes it concrete, after two years: Configuration Composition --- --- --- Inputs on 11 1 ( ) Visual states the library owns 384 3 Stories needed for real coverage Impossible 3 Requests that need a library release Most Almost none Layouts the library never anticipated Unsupported Free Cost of an unanticipat…