Skip to content
Design Engineering
Systems

Reuse behavior without losing control

Choose and adapt a component library by ownership, interaction quality, customization cost, and maintenance.

Why this matters

A library can save you from rebuilding tricky interaction behavior. It still has to fit your product, your component edges, and how you maintain code.

What to understand

Know what you receive and what stays yours: headless primitives give you behavior and semantics while you own composition and verification; copied source gives you code while you own local changes and upstream fixes; styled components give you a visual baseline while you own theme fit and upgrades.

Before adding anything, find your existing wrappers, token mappings, form controls, overlays, and layout shells. Use the component you already have if it solves the behavior.

Watch for

  • Replacing a primitive's required DOM or dropping required parts and breaking accessibility.
  • A second library bringing competing focus managers, portals, style resets, and theme habits.
  • Wrapping just to rename every prop with no product rule added.
  • Feature logic baked into a generic piece because only one feature uses it today.
  • Selectors tied to generated class order or inner DOM breaking across upgrades.
  • Assuming every install matches the docs instead of checking what your repo uses.

Strong default

Try the component in the hard case first: a menu inside a dialog, a long crew list, a narrow form. Ask your agent to check keyboard behavior and screen-reader naming before you spend time on visual styling. Customize through tokens, documented slots, variants, and component APIs.

When this doesn't apply

Headless doesn't mean "renders nothing" — a primitive may render the DOM and attributes its behavior needs. Copied code deliberately changed is fine, but note why it differs and how upstream fixes reach it.

In practice

ModelWhat you receiveWhat remains your responsibility
Headless primitivesBehavior, semantics, and unstyled rendered elements.Composition, styles, content, integration, and verification.
Copied sourceComponent code committed into your repository.Local changes and applying relevant upstream fixes.
Styled componentsA visual baseline and supported theme/customization APIs.Theme fit, supported variations, and upgrade compatibility.

A thin product wrapper can set approved tokens and variants while keeping the primitive's agreement intact. A feature piece then adds product behavior on top.

Example ownership chain
Dialog primitive       focus and modal interaction
Product Dialog         surface, spacing, title and action conventions
Invite dialog          recipient, role, validation and request state
Server operation       permission, persistence and delivery contract

The current shadcn/ui documentation describes its source distribution and component choices. Check what your repo actually uses instead of assuming every install has the same underlying primitive.

Verify

Labels, focus entry and return, dismissal, scrolling, long content, themes, and reduced motion where they apply. A library's accessibility work doesn't prove your wiring is accessible: a missing label or wrong trigger can still block the task.

Use $system to inspect duplication and drift; $component to define the boundary of a needed extension. Design tokens explains the shared visual contract.

Last updated on

On this page