What specific assumption does a component library's "reusability" promise rest on, and when does that assumption break down?
The reusability promise rests on the assumption that "the contexts this component will be used in later stay reasonably similar to the context it was originally designed for." This assumption usually holds early in a product's life, since functionality hasn't expanded much yet and the component doesn't face much variation in context. But as a product grows and features multiply, the assumption gradually stops holding — a component originally built to handle only the simplest scenario gets asked to accommodate more and more new variations.
The point where this assumption breaks down usually isn't a clear moment in time — it's a gradual process. Adding one or two new props at the start doesn't feel like a problem, but once a component accumulates a dozen or twenty props with combinatorial relationships that get hard to track, that component has effectively transformed from "a simple, reusable building block" into "a complex parameter system," and its maintenance cost has quietly exceeded the cost it was originally supposed to save.
"30% of components have never been used" sounds wasteful — does that mean the component library itself was poorly designed?
Not entirely — this number has at least two completely different underlying causes that need to be judged separately. The first is healthy dormancy: some components genuinely were designed for a specific scenario that never actually persisted or became widespread, so the component naturally never got used. In this case, sitting unused is actually a good outcome — it means the original judgment of "don't build a component for every hypothetical scenario" was correct, and forcing those components into use would be the real waste.
The second is dormancy caused by a communication failure: the component genuinely exists and genuinely could solve a problem some team is facing, but that team doesn't know it exists, or knows but finds building a local custom version faster than figuring out how to use it. This second kind is the one actually worth worrying about, and the fix usually isn't cutting these components — it's improving their discoverability, like labeling more clearly in the documentation what scenarios a component fits, or making sure there's a smooth channel that leads teams to check the component library when a similar need comes up, rather than jumping straight to writing something themselves.
Given how high the review cost is for extending a shared component, wouldn't it be better to just relax review and let every team freely modify shared components?
This approach creates a completely different, but equally serious, problem. If every team can freely modify shared components, one team adjusting a component's behavior for their own needs might unknowingly break how another team was already relying on it — this is exactly why review exists for shared components in the first place. Review isn't unnecessary bureaucracy — it's the step that ensures a change doesn't ripple out and break other consumers. Fully relaxing review just swaps "the component library is out of sync with reality" for "the component library itself becomes unstable and unpredictable," without actually fixing the underlying gap.
A more practical approach is making the review process itself faster and lighter, rather than removing it entirely. For example, design different levels of review rigor for "adding an optional prop that doesn't affect existing usage" versus "modifying a core behavior that might affect existing usage" — the former can go through a faster path, while the latter is where full cross-team confirmation is genuinely needed. This way, most day-to-day extension requests can be handled quickly, and only genuinely high-risk changes need to pay the corresponding review cost, rather than both being locked into the same process.
If I'm generating screens with an AI design tool, could component library bloat get worse, since AI generates so much faster?
It could get worse, or it could actually improve — the key is whether the AI is required to check against an existing component library when generating. If an AI generates a new screen with no imported reference to an existing component library at all, it's guessing statistically each time to produce something that "looks like a button" — which is effectively adding another team member who never checks the component library and just builds their own version each time, except producing at a far faster rate than a human engineer, so bloat only accelerates.
But the reverse is also true: if the generation process explicitly imports an existing component library and requires the AI to prioritize existing components, only suggesting a new one when there's genuinely no suitable option, AI can actually play the role of a tireless auditor — it can be required to check, before every generation, whether this request could be met by an existing component with an existing prop combination, rather than defaulting to generating something entirely new every time. This means whether component library bloat gets worse from adopting an AI tool depends on whether a team has explicitly written "check existing components first" into the AI's generation process — it's not something that happens automatically.
An engineer running a component library audit at their own team found a set of numbers worth every team relying on a component library taking note of: a single button component, originally designed to be "reusable," had spawned 23 different prop combinations across 47 separate implementations over 18 months; a bundle size analysis showed teams were importing the entire component library while only actually using 15 to 20 percent of it; overall component adoption plateaued at around 67 percent after the first year, with teams continuing to build custom implementations for newly emerging "edge cases" rather than reusing existing components. What this reveals isn't that this particular team was especially careless — it's that the concept of a "reusable component" itself carries an easily overlooked hidden assumption.
The core promise of a component library is "build once, use everywhere," and that promise only holds if the future contexts in which a component gets used stay reasonably similar to the context it was first designed for. In reality, product functionality keeps expanding, new use cases keep showing up — a button originally only needing to handle "text plus icon" gradually gets asked to handle a loading state, an icon-on-the-right layout, a red variant for destructive actions, a larger tap target on mobile. Each new requirement looks reasonable on its own, but if a component's core design didn't leave enough flexibility to accommodate this kind of change, the result is that every new requirement becomes another prop, and over time, a once-simple component turns into a bewildering set of parameter combinations.
The key reason component adoption plateaus is often not that teams don't know a library exists — it's that the communication and review cost of extending an existing component is sometimes higher than just writing a new one from scratch. If modifying a shared component requires a full review cycle and coordination with every other team already using it to confirm nothing breaks, while writing a local version only needs a nod from your own team, the rational choice is often the latter — even though that choice widens the gap between the component library and what's actually running in the codebase. This also explains why the statistic "30% of components have never been used" isn't entirely bad news: some of those components genuinely were over-engineered, solving problems that never persisted, and sitting unused is actually the correct signal. But another portion might simply be components teams didn't know existed, or knew about but found customizing faster anyway.
Simplifying this phenomenon down to "the component library is poorly managed" can lead to the assumption that the fix is stricter limits on adding new components and more rigorous review for every addition. But the real root cause is that most component libraries lack a continuous feedback mechanism that lets "an engineer hitting a real-world situation where an existing component isn't enough" effectively feed back into the library's own evolution, rather than turning into a local workaround nobody ever sees again. Without that feedback loop, a component library's evolution falls further and further behind the actual evolution of product requirements, and that gap is the true root cause behind 23 variants and a plateaued 67% adoption rate.
If your team is using or considering building a component library, the question worth asking first isn't "how many components should go into it" — it's "how smooth is the process when an engineer discovers an existing component isn't enough and needs to report or extend it." If the answer is "a full review cycle, possibly weeks of waiting," then no matter how well-designed the component library itself is, it's likely headed toward the same fate as the case above over the long run. Regularly auditing actual component usage — which components are genuinely referenced, which have never been used, which are being heavily worked around with custom implementations — is worth turning into a standing routine, rather than something only handled once the library has already bloated past the point of easy maintenance. That audit cost is far cheaper than discovering after the fact that the entire component library has drifted away from the actual codebase.