A Shared UI Component Library Across Products
A shared UI component library across products cuts build time and keeps a portfolio coherent. Here is what to share, what to leave per-product, and how to avoid rigidity.
A shared UI component library is one of the highest-return pieces of a portfolio foundation, and one of the easiest to get wrong. Done right, every product gets buttons, forms, tables, modals, and empty states that already work, are already accessible, and already look like they belong to the same company. Done wrong, it becomes a straitjacket that fights every product's specific needs until people route around it and you are back to twenty separate stacks. The line between the two is what you choose to share.
Should I share a component library across products?
Yes, if you run more than a couple of products. UI is where duplicated effort is most visible and most wasteful. Every product needs a button that handles loading, disabled, and error states. Every product needs form inputs with validation and accessible labels. Building those from scratch per product is pure waste, and worse, each copy drifts, so your products slowly stop looking related.
A shared library fixes both. You solve the boring, universal primitives once, to a high standard, and every product inherits them. That is exactly the logic that makes each next venture cheaper to ship: the shared layer absorbs the work you would otherwise repeat. Accessibility is the clearest example. Getting focus management, keyboard navigation, and ARIA right is genuinely hard. Do it once in the shared components and every product is accessible by default instead of never.
What belongs in the shared library, and what does not
The mistake that kills component libraries is sharing too much. Share the primitives. Do not share the pages.
Belongs in the shared layer:
- Primitives: buttons, inputs, selects, checkboxes, tooltips, the low-level pieces that are the same everywhere.
- Composites that are genuinely universal: modals, toasts, form field wrappers, data tables, pagination.
- Design tokens: color, spacing, typography, radius. These enforce coherence without dictating layout.
Stays per-product:
- Page layouts and flows, which are where products actually differ.
- Domain-specific components that only make sense for one product.
- Anything still changing shape, because a component that is not stable yet is not ready to be shared.
This is the same judgment as deciding what belongs in a shared foundation generally. Share the stable and universal. Keep the volatile and specific local. A shared component that has to grow a new variant every time a product touches it was never a shared component. It was three different components wearing a trench coat.
How to keep a component library from becoming a straitjacket
The reason people abandon shared libraries is rigidity. A designer needs a button that is slightly different, the shared button will not allow it, and now they either fork it or fight it. Either way the library lost.
Build for extension. Every shared component should accept style overrides and let products pass through their own props and children. The library provides the accessible, working baseline; the product decides how it looks and behaves at the edges. Design tokens do a lot of this work: a product that needs a different accent color changes a token, not a component. This is the paved road, not a locked gate. The shared path is the easy default, but stepping off it when a product genuinely needs to is allowed and cheap.
Rigidity also comes from bad versioning. If updating the shared library forces every product to update at once, people stop updating. Products should pin the library version and upgrade on their own schedule, the same way you version any shared foundation. A visual tweak for one product should never be able to break another's layout on the next deploy.
Treat it like a product, ship it like one
A component library only compounds if it is maintained like a real internal product, with a changelog, documented components, and someone who owns its quality. A library nobody maintains rots into a set of components people are afraid to touch, which is worse than no library at all.
I keep mine tight by generating new products against the shared library from day one, so drift never gets a foothold. My build platform scaffolds a new product already wired to the shared components and tokens, so Bootspring makes the coherent path the fastest path. When the easy way and the consistent way are the same way, you get a portfolio that looks like one company built it, because one operator did, once, and every product inherited it.