FrontalCode All articles
Frontend Frameworks

Your Component Library Is Quietly Killing Your Team's Velocity

FrontalCode
Your Component Library Is Quietly Killing Your Team's Velocity

Photo: software developer frustrated at computer with complex code on screen, via i.pinimg.com

Every frontend team reaches the same crossroads eventually. You're three months into a project, the design system is growing, and someone suggests: "Let's just build a shared component library." The room nods. Makes sense, right? Consistency, reusability, faster onboarding. What's not to love?

Fast forward a year and that same library has become the thing your team dreads touching. PRs sit open for two weeks because nobody wants to own the consequences of a breaking change. New engineers spend their first sprint just figuring out how to extend a Button component to handle an edge case the library never anticipated. Sound familiar?

Component libraries are a genuinely good idea — in theory. In practice, they carry hidden costs that don't show up in your initial architecture meeting. Let's dig into where things go sideways and how to course-correct before the abstraction becomes a full-blown bottleneck.

The Abstraction Tax Nobody Budgets For

When you abstract UI into a shared library, you're making a bet: that the upfront investment in generalization will pay off in reuse. Sometimes that bet pays out. Often, it doesn't — at least not the way you expected.

The problem is that abstractions need to be maintained. Every time your product evolves — new brand guidelines, an accessibility audit, a design overhaul — your component library has to evolve with it. That sounds fine until you realize the library is consumed by four different apps, two of which are running a version from eight months ago. Now you're not just shipping a UI update; you're managing a minor open source project inside your own organization.

This is what engineers sometimes call the "internal open source trap." You get all the overhead of maintaining a public package — versioning, changelogs, backward compatibility concerns — without any of the community contributions. Your senior engineers end up playing library maintainer instead of shipping features.

When "Buy" Beats "Build"

Before you commit to rolling your own, it's worth being brutally honest about your team's actual needs. Third-party libraries like Radix UI, shadcn/ui, or Headless UI have matured considerably. They handle accessibility, keyboard navigation, and ARIA patterns in ways that are genuinely hard to replicate without dedicated effort.

The case for building your own library gets stronger when:

For most teams — especially startups and mid-sized product companies — the honest answer is that a well-configured third-party library with a solid theming layer will outperform a homegrown solution for years. The engineering hours saved are better spent on product differentiation.

Auditing What You Already Have

If you're already deep in a custom library, the first step isn't a rewrite — it's an audit. You need to understand what you're actually dealing with.

Start by mapping usage. Which components are being used across more than one app? Which ones have only a single consumer? A component that lives in a shared library but only gets used in one place isn't really a shared component — it's a component with extra steps.

Next, look at your issue tracker. Which components generate the most bug reports or feature requests? High-churn components are a signal that the abstraction isn't holding up to real-world usage. Either the component is trying to do too much, or it's too rigid to accommodate legitimate variation.

Finally, run a bundle analysis. Tools like [webpack-bundle-analyzer](https://en.wikipedia.org/wiki/Webpack) or Vite's built-in rollup visualizer will show you exactly how much of your library is actually making it into production builds. If you're shipping 40kb of component code and only using 15kb of it, that's a conversation worth having.

Designing Libraries That Don't Fight You

If you're committed to maintaining a component library — or building a new one — a few principles will save you a lot of pain down the road.

Favor composition over configuration. A Modal component that accepts a header, body, and footer slot is almost always more flexible than one that takes 15 props to control every possible layout variation. Composition lets consumers customize behavior without requiring library changes.

Draw a hard line between primitive and opinionated components. Primitive components (buttons, inputs, layout containers) should be highly flexible and loosely styled. Opinionated components (a UserProfileCard, a CheckoutSummary) should live closer to the consuming app, not in the shared library. When opinionated components end up in a shared library, they calcify product decisions in a place that's hard to change.

Version thoughtfully, not religiously. Not every change needs a major version bump. Establish clear internal conventions about what constitutes a breaking change and communicate them. A lightweight decision log in your repo goes a long way toward reducing the "why did this change?" confusion that slows teams down.

Make it easy to eject. This one sounds counterintuitive, but the best component libraries are ones you could theoretically stop using without a catastrophic refactor. If a consuming app can copy a component out of the library and own it locally when needed, you've built something healthy. If extraction feels impossible, your library has become a dependency trap.

The Right Tool at the Right Scale

Component libraries aren't inherently good or bad — they're tools, and like any tool, their value depends entirely on how well they match the problem at hand. A hammer is great until you need a screwdriver.

The teams that get the most out of shared component libraries are the ones that treat them like a product: with real ownership, clear scope, and honest evaluation of whether the investment is paying off. The teams that struggle are the ones that built a library because it seemed like the right thing to do, then never revisited whether it was still serving them.

So pull up your metrics, talk to the engineers who use it every day, and ask the uncomfortable question: is this library making us faster, or is it just making us consistent in our slowness?

There's a big difference between those two things.

All Articles

Related Articles

TypeScript Is a Tool, Not a Religion: Knowing When to Loosen the Reins

TypeScript Is a Tool, Not a Religion: Knowing When to Loosen the Reins

Picking Your Rendering Side: A Practical Guide to Server vs. Client Components

React, Vue, or Svelte? A No-Nonsense Framework Breakdown for Teams That Actually Ship

React, Vue, or Svelte? A No-Nonsense Framework Breakdown for Teams That Actually Ship