Back to Design Systems
Topic 8 of 8
Architecture & Scaling
Mono-repos, versioning, distribution, and scaling design systems across teams
Scaling Design Systems
As your design system grows beyond a single team, you need solid architecture for versioning, distribution, and multi-team collaboration. A well-architected system can serve hundreds of developers across dozens of projects.
🏗️ Architecture Decisions
Monorepo
All packages in one repo (Turborepo, Nx)
Multi-repo
Separate repos per package
Single Package
Everything bundled together
Federated
Distributed ownership across teams
Monorepo Setup with Turborepo
# Create a new Turborepo
npx create-turbo@latest my-design-system
# Folder structure
my-design-system/
├── apps/
│ ├── docs/ # Storybook or documentation site
│ └── playground/ # Test/demo app
├── packages/
│ ├── tokens/ # Design tokens
│ ├── ui/ # React components
│ ├── utils/ # Shared utilities
│ └── config/ # Shared configs (ESLint, TypeScript)
├── turbo.json
├── package.json
└── pnpm-workspace.yaml
# turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": {
"dependsOn": ["build"]
}
}
}
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
# Run all builds
pnpm turbo run build
# Run only changed packages
pnpm turbo run build --filter=...[HEAD~1]
Package Structure
// packages/ui/package.json
{
"name": "@acme/ui",
"version": "1.0.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./button": {
"import": "./dist/button.mjs",
"require": "./dist/button.js",
"types": "./dist/button.d.ts"
}
},
"sideEffects": ["**/*.css"],
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@acme/tokens": "workspace:*",
"tsup": "^7.0.0",
"typescript": "^5.0.0"
},
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"dev": "tsup src/index.ts --format cjs,esm --dts --watch"
}
}
// packages/ui/src/index.ts
export * from './Button';
export * from './Card';
export * from './Input';
// ...
// packages/ui/tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts', 'src/button.ts'],
format: ['cjs', 'esm'],
dts: true,
splitting: true,
clean: true,
treeshake: true,
external: ['react', 'react-dom'],
});
Versioning with Changesets
# Install changesets
pnpm add -Dw @changesets/cli
# Initialize
pnpm changeset init
# When you make a change, add a changeset
pnpm changeset
# This prompts: What packages changed? Major/minor/patch? Description?
# Creates a file like:
# .changeset/brave-eagles-dance.md
---
"@acme/ui": minor
"@acme/tokens": patch
---
Added new Avatar component with image fallback support.
# Versioning workflow
pnpm changeset version # Bumps versions, updates CHANGELOG
pnpm changeset publish # Publishes to npm
# In CI (GitHub Actions)
- name: Create Release PR or Publish
uses: changesets/action@v1
with:
publish: pnpm changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
Publishing to npm
# .npmrc for private registries
@acme:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
# Or use npm's public registry
# Ensure package.json has:
{
"name": "@acme/ui",
"publishConfig": {
"access": "public"
}
}
# Build and publish script
# package.json (root)
{
"scripts": {
"release": "pnpm build && pnpm changeset publish"
}
}
# GitHub Actions workflow
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
- run: pnpm install
- run: pnpm build
- name: Publish
uses: changesets/action@v1
with:
publish: pnpm changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Multi-Team Contribution
// CODEOWNERS file for GitHub
# .github/CODEOWNERS
# Core team owns everything by default
* @acme/design-system-core
# Specific component ownership
/packages/ui/src/data-table/ @acme/data-team
/packages/ui/src/charts/ @acme/analytics-team
/packages/tokens/ @acme/design-team
# Contribution Guidelines (CONTRIBUTING.md)
## Adding a New Component
1. Create a proposal issue using the template
2. Get approval from design-system-core team
3. Create component following patterns in /packages/ui/src/Button
4. Add Storybook stories
5. Add unit tests
6. Add changeset
7. Open PR and request review
## Component Checklist
- [ ] TypeScript types exported
- [ ] Storybook story with all variants
- [ ] Unit tests with >80% coverage
- [ ] Accessibility tested (keyboard + screen reader)
- [ ] Documentation in Storybook MDX
- [ ] Changeset added
Consuming Design Systems
# Installing the design system
npm install @acme/ui @acme/tokens
# In your app
import { Button, Card } from '@acme/ui';
import '@acme/ui/styles.css'; // Or import tokens
// Tree-shaking: only imports what you use
import { Button } from '@acme/ui/button';
// Version pinning strategies
// package.json
{
"dependencies": {
// Exact version (safest, but manual updates)
"@acme/ui": "2.1.0",
// Caret: allows minor updates (recommended)
"@acme/ui": "^2.1.0",
// Tilde: allows patch updates only
"@acme/ui": "~2.1.0"
}
}
// Renovate config for auto-updates
// renovate.json
{
"extends": ["config:base"],
"packageRules": [
{
"matchPackagePatterns": ["@acme/*"],
"automerge": true,
"automergeType": "branch"
}
]
}
Design System Tooling
Turborepo
High-performance monorepo build system
Nx
Full-featured monorepo toolkit with generators
tsup
Zero-config TypeScript bundler powered by esbuild
Changesets
Versioning and changelog management for monorepos
Chromatic
Visual testing and review for UI components
💡 Scaling Tips
- • Start small: don't over-engineer early
- • Establish clear ownership with CODEOWNERS
- • Use semantic versioning strictly
- • Document breaking changes thoroughly
- • Provide migration guides for major versions
- • Set up automated visual regression testing
- • Create contribution guidelines early
- • Use feature flags for gradual rollouts