Tips & Tricks
Managing Variants in a Mono-repo
Why component variant ownership becomes a bottleneck in mono-repos, and a git-inspired approach to fixing it.
The problem
Mono-repos thrive on shared code. A design-system team publishes a Button, and every product team imports it. Then one team needs a Button with a loading spinner, another needs one with an icon slot, and a third needs a compact variant for data-dense tables.
The natural instinct is to add these variants to the shared component. A few props here, a conditional branch there. Before long the component is a swiss-army knife — and the design-system team owns every blade.
Ownership gets inverted
Because the component lives in the design-system package, the design-system team are the code owners. Every variant, no matter which product team requested it, goes through their review queue. They become the bottleneck for changes they have no context on, and the gatekeepers for code they didn’t write.
Worse, an update to the base component can break variants in other packages. The design-system team refactors the internal markup of Buttonand suddenly the compact table variant in the analytics package is broken. The people who understand the breakage aren’t the people who caused it, and the people who caused it didn’t know the variant existed.
Copy-paste isn’t the answer
The obvious escape hatch is to copy the component into your own package and modify it there. This solves ownership — you own your copy — but creates a new problem: drift. When the upstream component gets a bug fix, an accessibility improvement, or a design token update, your copy doesn’t get it. Over time you’re maintaining a fork with no merge strategy.
What you actually want
The ideal setup has two properties:
- Single source of truth — the base component lives in one place and upstream changes propagate to variants.
- Distributed ownership— each team owns their variant and can modify it without going through the design-system team’s review queue.
This is the same problem git solves for codebases: a shared mainline with independent branches that can be rebased.
variant-it: git-style branching for components
variant-it is a CLI that applies this model to component variants. It has three commands:
Branch— scaffold a new variant from a source component. The variant is a full copy that lives in your team’s package, under your code ownership. A variant.json file tracks where it came from and snapshots the source at branch time.
npx variant-it branch packages/design-system/Button packages/analytics/CompactButtonRebase— pull upstream changes into your variant. Under the hood, variant-it uses git’s three-way merge to apply only the changes that happened upstream since you branched, leaving your local modifications intact. If there’s a conflict, you get familiar merge markers to resolve.
npx variant-it rebase packages/design-system/Button packages/analytics/CompactButtonList — see all variants branched from a given source, so the design-system team has visibility into who is consuming their components without being responsible for those consumers.
npx variant-it list packages/design-system/ButtonHow ownership changes
With this model, the design-system team owns the base component and nothing else. Product teams own their variants. When the base changes, teams rebase at their own pace — the same way you pull upstream changes into a feature branch.
The --all flag lets the design-system team broadcast a rebase across every variant in the repo, and downstream variant chains are protected by default — variant-it will refuse to rebase a component that has its own downstream variants unless you --force it, preventing cascading breakage.
The result is a mono-repo where shared code stays shared, ownership stays distributed, and nobody is blocked on a team that doesn’t have context on their change.