We tried strict semver with a monorepo approach first (all components in one package, bump major for any breaking change). Worked fine at v1-v3 then became a nightmare. Every button color tweak or spacing adjustment felt dangerous because major bumps meant consumer apps had to actively upgrade and test everything.
Switched to independent package versioning last year. Each component is its own npm package. Breaking changes are isolated to that package, minor bumps happen constantly, nobody panics.
The reality: most breaking changes in design systems aren't actually breaking for 80% of consumers. Button props get deprecated but your app probably doesn't use that prop. With independent versioning you can deprecate aggressively and let teams upgrade on their own schedule. With monorepo semver you're held hostage by the most conservative consumer.
We use deprecation warnings in the code itself (console.warn in dev, just works), keep a detailed changelog per package, and actually test our own breaking changes against a few key consumer apps before shipping.
monorepo semver sounds cleaner on the whiteboard. independent packages with a governance doc for deprecation policy actually works in reality.
Independent packages are definitely the move, though I'd flag that you're trading one problem for another. Yeah, monorepo semver becomes theater fast, but now you're managing dependency hell across 30+ packages and your consumers have to track updates for each one.
The real win isn't the versioning scheme, it's that breaking changes become localized. We did this with our embedding model pipeline and it changed everything operationally. Component A breaking doesn't cascade to component B's consumers.
What bit us later: consumers pinning old versions because updating one package means auditing five dependencies. Document your deprecation path hard or you'll own support headaches forever.
Jake Morrison
DevOps engineer. Terraform and K8s all day.
We did the exact same thing with our internal component lib. Monorepo semver looked clean on paper until you're doing major bumps every sprint because someone tweaked a margin.
Independent packages solved it. Yeah you get more packages to manage, but the friction dropped dramatically. Teams can adopt updates on their own schedule instead of waiting for some coordinated upgrade dance.
The trick is good tooling for managing the bunch (we use Lerna, some use Nx). Once that's sorted, the operational burden is way lower than fighting false alarms about breaking changes that nobody actually cares about.