1. 04The UI kit: primitives, styles, and interactions
  2. 05Building components with clear responsibilities
  3. 06Shared abstractions versus local composition
  4. 07When a component library needs an escape hatch
  5. 08Third-party UI dependencies are infrastructure too

Component libraries and UI infrastructure

07

When a component library needs an escape hatch

Exporting lower-level primitives alongside turnkey components gives product teams the freedom to build non-standard layouts without bloating library APIs.

By Eyal Ellenbogen August 14, 2026 3 min read

A good component library earns trust by making common patterns effortless. When a ConfirmDialog component bundles a backdrop, focus trap, Escape key handling, and standard action buttons, product teams get an accessible modal in three lines of code.

The problem begins with the first non-standard requirement. A feature needs a multi-step onboarding wizard inside a popup, a drawer with a custom progress stepper, or a modal that replaces standard footer buttons with a carousel.

Without an escape hatch, we face an unproductive tradeoff: petition library maintainers to add props like hideFooter and renderCustomHeader, compromise on the UX to fit the default, or hack together a half-baked custom solution.

The Lego pieces behind the set

An escape hatch is neither a private API leak nor permission to fork component source. We can think of it like a Lego set: the library ships the assembled model out of the box, but keeps the individual bricks publicly available.

When a design system builds a dialog, it should construct it from public building blocks:

// The assembled set:
<ConfirmDialog title="Delete member" onConfirm={handleDelete} />

// The individual bricks:
<DialogOverlay>
  <DialogSurface>
    <WizardStepper step={currentStep} />
    <WizardStepContent />
    <DialogClose />
  </DialogSurface>
</DialogOverlay>

By exporting DialogOverlay, DialogSurface, and DialogClose, the library gives feature teams the freedom to construct multi-step wizards or drawer layouts. At the same time, the library retains ownership of focus trapping, scroll locking, and ARIA dialog semantics.

New Lego set vs. free build

When a product team asks for a variation that the default component does not support, the right response depends on how broadly the pattern applies:

  • Box a new official set: When multiple product workflows need the same arrangement, such as a wizard dialog with a stepper, the design system team can design the interaction states and ship a dedicated composed default.
  • Support the free build: When an unusual layout belongs to a single workflow, the feature team can assemble it locally using the library's exported bricks. The shared component remains simple, and we get the layout we need without waiting on a design system release.
  • Keep domain logic out: If the request involves fetching data, evaluating permission roles, or triggering route navigation, that logic stays in the feature module. Shared UI infrastructure provides visual and interaction building blocks, never domain workflow rules.

The balance between sets and bricks

Watch what developers actually import.

If nobody touches the building blocks, teams might be working around the system instead of with it. If nobody uses the composed defaults, the library is failing to solve the common cases.

A great design system does not try to predict every screen with props. It gives teams a ready-to-use default for normal workflows, and keeps the raw pieces public for when reality gets messy.