Skip to content
Frontend system design ยท about 45 minutes

Shared component library

Design a shared component library, a design system, for a company with several product teams. Today each team builds its own buttons, forms and modals, so the products look different and accessibility is uneven. The library must provide common components and styles, support a light and a dark theme, and be adopted by existing React applications step by step. A small core team maintains it, and product teams must be able to contribute.

Ask first

  • How many teams and applications will use it, and are all of them built with React?
  • Is there an existing visual language and a design tool library that the code must match?
  • Which themes are needed: light and dark only, or several brands too?
  • How do the applications write styles today: CSS modules, utility classes, CSS-in-JS?
  • Who owns the library, and how are changes requested and approved?
  • Which accessibility level is the target, for example WCAG 2.2 level AA?

The outline

Write your own outline for each part first, then open it to compare with the points a strong answer covers.

Requirements
  • Functional: design tokens, base components (button, input, select, modal, tabs, toast), layout helpers, icons and documentation
  • Accessible by default: each component meets the target level with no extra work from the product team
  • Themes: light, dark and brand themes switch at run time without a new build
  • Step-by-step adoption: it works next to existing styles, one component at a time
  • Stability: clear versions, a changelog and a migration path for breaking changes
  • Low cost: importing one button does not add the whole library to the bundle
Architecture
  • Three layers: design tokens, then base components, then composed components and patterns; each layer uses only the layers below it
  • Tokens are the single source: defined once as data, then built into CSS custom properties and TypeScript constants
  • A theme is a set of token values; a switch changes the CSS variables on a root element, so the components do not render again
  • One repository with packages for tokens, components and icons, published with semantic versions
  • Behavior (focus, keyboard, state) is separate from style, so complex widgets are built once and tested well
  • A documentation site, built from the same code, shows live examples, props and usage rules
Data model
  • Token levels: raw values (blue-600), semantic tokens (color-text-primary, color-bg-danger), and component tokens only when needed; components use semantic tokens
  • Token types: color, spacing, type scale, radius, shadow, motion duration, breakpoints, z-index
  • Variants are a small closed set (variant, size, tone), typed as string unions and not as free strings
  • States for each component: default, hover, focus-visible, active, disabled, loading, invalid; each has a defined look
  • Metadata for each component: status (experimental, stable, deprecated), the version that added it, and its owner
Interface
  • One naming convention everywhere: variant, size, disabled, onChange; the same word means the same thing in every component
  • The root element accepts className, native props and a ref, and the prop types extend the native element's types
  • Form components work controlled (value and onChange) and uncontrolled (defaultValue)
  • Composition before configuration: Modal with Modal.Header and Modal.Body, Tabs with Tab and Panel
  • Package contract: named exports, ES modules, type definitions, and no side effects on import, so tree shaking works
  • Deprecation contract: a prop or component is marked deprecated and warns in development for one major version before it is removed
Optimizations
  • Accessibility tests for each component: automated checks, keyboard tests, and manual screen reader checks for complex widgets
  • Visual regression tests on the documentation examples catch style changes that nobody planned
  • Bundle: tree shaking, no heavy dependencies, static CSS in place of styles built at run time, and a size check in CI
  • Releases: semantic versions, a changelog, codemods for breaking changes and a deprecation period
  • Adoption: tokens and the most used components first; usage is measured across repositories; old and new components can live side by side
  • Contribution: a written proposal, a review by the core team, and a clear rule for what belongs in the library
  • Theme details: contrast is checked in every theme, prefers-color-scheme and prefers-reduced-motion are respected, and there is no flash of the wrong theme on load

Pitfalls

  • Starting with fifty components, before the tokens and a few well-made components exist
  • Components with a boolean prop for every team's special case
  • Colors and spacing written directly in components, which blocks theming
  • Breaking changes with no versions and no migration path, so teams stop upgrading
  • Treating it only as code: no owner, no documentation and no contribution process

Follow-ups an interviewer asks

  • A team needs a button variant that the library does not have. What is the process?
  • How do you release a breaking change to twelve applications?
  • How does the dark theme work technically, and how do you avoid a flash of the wrong theme on load?
  • How do you make sure that each component is accessible and stays accessible?
  • Would you build the modal and the select yourself, or use a headless library? Why?
  • How do you know that the design system is a success?