How to build a web component library

Architecture, frameworks, versioning and testing for a library that scales with your product

10 min

A component library is the technical embodiment of a design system. It is where tokens and design decisions become reusable code that product teams consume directly. Building it well is the difference between accelerating development and generating more technical debt.

This guide covers the key decisions: which technology to use, how to structure the architecture, how to version and publish, how to test UI components, and how to document them so other teams adopt them without friction.

Component library architecture

Architecture determines how components are organised, built and distributed. The two main approaches are monorepo (all components in a single repository with independent packages) and single-package (one npm package containing all components).

The monorepo is the most scalable option for large teams. Tools like Turborepo, Nx or Lerna handle inter-package dependencies, run incremental builds and publish independent versions. The single-package approach is simpler to maintain when the team is small and the library has fewer than 30–40 components.

  • Monorepo: scalable, independent versioning per component, incremental builds
  • Single-package: simple, less configuration overhead, ideal for small teams
  • Internal structure: each component in its own folder with code, tests, stories and docs

React, Vue, Web Components: what to choose

The technology choice depends on your organisation’s ecosystem. If all your products use React, building the library in React is the most pragmatic decision. If you have products across multiple frameworks, Web Components or an abstraction layer like Mitosis deserve consideration.

Web Components offer native interoperability with any framework, but their testing and tooling ecosystem is not yet as mature as React’s. Lit (by Google) is the most popular framework for building Web Components with a modern DX. In practice, many organisations opt for React and provide wrappers for other frameworks when needed.

  • React: most mature ecosystem, largest community, best tooling (Storybook, Testing Library)
  • Vue: excellent DX, single-file components, strong choice if your stack is Vue
  • Web Components: framework-agnostic, browser standard, steeper learning curve
  • Mitosis: compiles to React, Vue, Angular, Svelte from a single source — interesting but immature

Versioning and publishing

Semantic versioning (semver) is non-negotiable. Consumers of your library need to know whether an update is safe (patch), adds functionality (minor) or breaks the API (major). Use tools like Changesets or semantic-release to automate changelog generation and npm publishing.

Publish to a private npm registry if the library is internal, or to public npm if it is open source. Set up CI/CD so that every merge to main automatically generates a new version based on accumulated changesets. This eliminates the manual process and reduces errors.

Testing UI components

UI components require a specific testing strategy that combines unit tests, interaction tests and visual tests. Testing logic alone is not enough: you need to verify that the component renders correctly and that user interactions work as expected.

  • Unit tests: validate props, states and internal logic with Vitest or Jest + Testing Library
  • Interaction tests: simulate clicks, inputs and keyboard navigation to verify behaviour
  • Visual regression tests: compare screenshots between versions to catch unintended changes (Chromatic, Percy, Playwright)
  • Accessibility tests: validate WCAG compliance with axe-core or pa11y integrated into CI

Documentation with Storybook

Storybook has become the standard for documenting and developing components in isolation. Each component has one or more "stories" that showcase its variants, states and use cases. Storybook addons let you add prop tables, interactive controls, accessibility checks and MDX documentation.

A good practice is writing stories during component development, not after. This forces you to think about the component API from the consumer’s perspective and catches design issues before they reach production.

Optimisation: tree shaking and bundle size

A component library that bloats consuming apps' bundles will face immediate pushback. Configure the build to support tree shaking: publish ES modules (ESM), mark the package as sideEffects: false in package.json, and avoid barrel imports that prevent dead code elimination.

Measure each component’s size with tools like bundlephobia or size-limit. Set a size budget per component and monitor it in CI to prevent regressions. A button should not weigh more than 2–3 KB gzipped.

Long-term maintenance

A component library is a product that needs continuous maintenance. Plan for dependency updates, deprecation of obsolete components, and migrations when framework versions change. Automate everything you can: Dependabot for dependencies, CI for tests and publishing, and alerts for size regressions.

Assign clear ownership: each component should have a responsible team or individual. When nobody owns a component, nobody maintains it.

Key Takeaways

  • Choose monorepo for large libraries and single-package for small teams
  • Framework choice should align with your organisation’s ecosystem
  • Use semantic versioning and automate publishing with Changesets or semantic-release
  • Combine unit, interaction, visual and accessibility tests
  • Optimise for tree shaking and monitor bundle size in CI
  • Assign clear ownership to ensure long-term maintenance

Need to build a component library?

We design the architecture, set up the tooling and build the foundational components so your team can scale with confidence.