Design System Versioning with Changesets
Semver strategy, monorepo releases, and consumer upgrade paths for design system packages.
Design SystemsIntermediate
Why
Unversioned design systems force consumers to pin commits or break on every pull. Semver + changesets give consumers predictable upgrades and let maintainers ship fixes without coordinating manual releases across teams.
According to the 2024 Spark Design Systems survey, 78% of organizations with a design system report faster product delivery. JavaScript is used by 62% of developers per the Stack Overflow 2024 survey and component libraries that align with design tokens cut handoff time by 60%.
When
Version from day one of publishing to npm. Even internal-only packages benefit — you'll thank yourself when a breaking change needs a coordinated rollout.
How
1. Install @changesets/cli — init creates .changeset/ folder and config
2. Add a changeset per PR — `npx changeset` prompts for affected packages + bump type
3. CI check — `changeset status` fails the build if a PR touches packages without a changeset
4. Release — `changeset publish` on merge to main, reads accumulated changesets, bumps + publishes
5. Snapshot releases — `changeset version --snapshot` for PR previews, e.g. [email protected]
6. Consumer upgrade — codemod for breaking changes, range "^1.0.0" for safe minors
Example
# .changeset/cool-cats.md
---
"@myorg/button": minor
"@myorg/tokens": patch
---
Add `loading` prop to Button; tokens fix gray-200 hex typo.
# package.json (consumer)
{
"dependencies": {
"@myorg/button": "^2.1.0",
"@myorg/tokens": "^2.1.0"
}
}
# .github/workflows/release.yml
on: { push: { branches: [main] } }
jobs:
release:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- run: pnpm install
- run: pnpm changeset publish
env: { NPM_TOKEN: ${{ secrets.NPM_TOKEN }} }
Common Problems
- ⚠Breaking change shipped as patch — consumers auto-upgrade and production breaks
- ⚠Monorepo packages versioned independently drift — Button v3 needs Tokens v2 but consumer has v1
- ⚠No codemod for breaking changes — every consumer hand-edits 50 call sites
Best Practices
- ✓Use changeset status in CI — block PRs that touch packages without a changeset
- ✓Ship a codemod with every breaking change — jscodeshift transform in the same release
- ✓Pin peer dependencies between related packages — Button declares peer "@myorg/tokens": "^2"