Design Systems at Scale
A design system is more than a component library—it's a shared language across teams. Building one that scales requires organizational alignment and technical discipline.
What a Design System Includes
1. Component library: Reusable UI building blocks (buttons, inputs, modals, cards).
2. Design tokens: Colors, typography, spacing, shadows—the primitives of design.
3. Documentation: Usage guides, best practices, do's and don'ts.
4. Governance: Version strategy, contribution process, breaking change policy.
5. Tools: Figma for design, Storybook for documentation, npm for distribution.
Organizational Structure
Design systems need dedicated ownership.
Option 1: Centralized team Single team owns the design system. Other teams consume it. Fast decision-making, consistent quality, but can become bottleneck.
Option 2: Distributed ownership Each product team maintains their components; a central team provides guidelines and tooling. More agile, but risk of inconsistency.
Hybrid (recommended): Centralized core components (button, input, card) owned by platform team. Domain-specific components (charts, tables) owned by feature teams. Shared patterns and conventions.
Component Design
Props-driven: Components should be flexible but opinionated.
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost'
size?: 'sm' | 'md' | 'lg'
icon?: React.ReactNode
loading?: boolean
disabled?: boolean
children: React.ReactNode
onClick?: () => void
}
export function Button({ variant = 'primary', size = 'md', ...props }: ButtonProps) {
// Implementation
}
Don't prop everything. A button with 30 props is hard to use and maintain. Prefer composition: smaller components combined.
Defaults matter: Most components used with default props. Make sensible defaults.
Extensibility: Allow custom styling without breaking encapsulation.
// Good: className prop for custom styling
<Button className="my-custom-class" />
// Bad: Exposing internals
<Button buttonClassName="..." innerDivClassName="..." />
Versioning Strategy
Semver: Major.Minor.Patch
- Major: Breaking changes (removed props, renamed components).
- Minor: New features, non-breaking changes.
- Patch: Bug fixes.
Example: v2.5.1 → v2.6.0 (new feature), v3.0.0 (breaking change).
Migration guides: Every major version needs a guide on upgrading.
# v2.0.0 Migration Guide
## Removed: LoadingButton component
Use `<Button loading={true} />` instead.
## Renamed: ButtonGroup → ButtonSet
Update imports and props.
## Changed: variant prop values
'primary' remains, but 'secondary' → 'neutral', 'ghost' → 'minimal'.
Documentation
Storybook is the standard:
// Button.stories.tsx
export default { component: Button }
export const Primary = {
args: { variant: 'primary', children: 'Click me' }
}
export const Secondary = {
args: { variant: 'secondary', children: 'Click me' }
}
export const Loading = {
args: { loading: true, children: 'Submitting...' }
}
Each story is interactive—engineers can test props in real-time.
Document the why: Why does this component exist? When should you use it vs. a similar one? What are the pitfalls?
Token Management
Centralize tokens in a single source of truth:
{
"colors": {
"primary": "#2563eb",
"primary-dark": "#1d4ed8",
"success": "#16a34a"
},
"spacing": {
"xs": "0.25rem",
"sm": "0.5rem",
"md": "1rem"
},
"typography": {
"heading-1": { "size": "2rem", "weight": 700 }
}
}
Generate code automatically:
// Design tokens → CSS variables
:root {
--color-primary: #2563eb;
--color-success: #16a34a;
--spacing-xs: 0.25rem;
}
// Design tokens → TypeScript enums
export enum Colors {
Primary = '#2563eb',
Success = '#16a34a',
}
Adoption and Governance
Mandate adoption: New projects must use the design system. Old projects gradually migrate.
Measure adoption: Track % of components from design system vs. custom. Target 80%+.
Regular reviews: Monthly design system office hours. Teams share needs, discuss roadmap, resolve blockers.
Contribution process: Clear guidelines for proposing new components, submitting PRs, getting reviewed.
Common Challenges
Design-engineering misalignment: Designers design, engineers implement. Differences emerge. Mitigate by involving both in design system decisions.
Over-engineering: Design systems can become bloated. Keep components focused. If a component has 20 variants, it's too generic.
Slow iteration: Design systems prioritize stability over speed. But teams need new features fast. Balance with periodic major versions (e.g., yearly).
Real Example
A fintech company's design system serves 150 engineers across 3 products. Centralized team owns 40 core components. Each product team owns 20 domain-specific components. Versioning follows semver. Storybook documents all components. Adoption is 85%; custom components are discouraged but allowed with approval. Design system velocity: 2 minor releases per quarter, 1 major release per year. Result: 40% faster feature delivery, consistent design language across products.
Frequently asked questions
Should I centralize or distribute design system ownership?
Hybrid approach works best: centralized core components (button, input), distributed domain-specific components (charts, forms). Centralized team provides guidelines and tooling.
How do I version a design system?
Use semantic versioning. Major for breaking changes, minor for new features, patch for bugs. Provide migration guides for major versions.
What's the right size for a core component library?
20-60 components is typical. More than 100 suggests you're over-engineering. Focus on high-reuse, high-value components.