skip to content

For a shared component library used by thirty product teams, how open should the API be to passthrough attributes, host references and style overrides?

level: principalimportance: should knowfreq 30%

answer

  1. every exposed internal is a promise
  2. closed, open, or curated
  3. one documented target element
  4. hatch usage as a signal
  5. forks are the real alternative

basics

~20 s

Open enough that teams never fork, closed enough that internals stay changeable. Most libraries curate: attributes pass to one documented element, a host reference points at the primary focusable node, style changes go through named override points, and escape-hatch usage is measured.

solid answer

~50 s

There is no single right answer, because each hatch trades **consumer flexibility** against the library's **freedom to change**. A fully closed API keeps components consistent and internals free to change, but teams under deadline fork or copy the component, which is worse. A fully open API — every attribute spread everywhere, internal structure styled freely — makes every internal a public contract. I would **curate**: pass unknown attributes to **one documented element**, forward a host reference to the **primary interactive node** and keep its kind stable, allow style changes only through **named override points**, and offer one explicit, searchable escape hatch for the rest. Then I would **measure** hatch usage across the thirty codebases: a hatch used by many teams marks a missing feature to build properly. The mix shifts with team count, product diversity and how much accessibility risk passthrough carries.

go deeper

for a junior

Know the three hatches — passthrough attributes, host references, style overrides — and that using the documented one is safer than reaching into internals.

for a middle

Explain why each hatch turns an internal detail into a contract, and why passthrough needs one documented target and written merge rules.

for a senior

Design a curated hatch set for a real library, guard accessibility attributes, and use code search to find repeated workarounds worth turning into features.

for a principal

Own the openness trade-off for the whole organisation: weigh forks against frozen internals, match the policy to consumer count and skill, and revisit it as the measurements change.

## The three kinds of escape hatch A component library's declared properties never cover every need. Three mechanisms let callers reach past them: - **Attribute passthrough** — unknown attributes (test identifiers, analytics tags, accessibility attributes, platform-specific extras) are forwarded to an underlying element. - **Host references** — a handle to the underlying node, so a caller can move focus to it, measure it, or anchor a positioned overlay to it. - **Style overrides** — a way to add or change styling on the component or its parts beyond the variants the API offers. Each hatch solves real problems. Each also turns something **internal** — which element exists, what it is, how it is styled — into something callers depend on. ## The two failure modes | Position | What goes right | What goes wrong | |---|---|---| | **Closed**: declared properties only | internals stay free to change; consistent output | teams blocked on a deadline copy or fork the component, and forks drift | | **Open**: everything forwarded, any styling | no team is ever blocked | every element, attribute target and internal style becomes a contract; accessibility attributes can be overwritten | | **Curated**: documented hatches only | flexibility where it is needed, contracts are explicit | requires design effort, review and measurement | The hidden cost of the closed position is that the alternative to a hatch is rarely "no customisation" — it is **a fork**, which the library cannot see, fix or upgrade. The hidden cost of the open position is that the library can no longer restructure a component without breaking someone; how such changes are classified belongs to the library's versioning policy, but the API decides how many of them there will be. ## A curated design 1. **One documented passthrough target.** For a multi-element component such as a trip-search field with a wrapper, a label and an inner text input, state which element receives unknown attributes — usually the interactive one — and keep that choice stable. 2. **Merge rules written down.** Caller handlers run alongside the library's rather than replacing them; caller style hooks are added to the library's rather than replacing them; for accessibility attributes the library sets, decide and document whether the caller may override them. 3. **A host reference to the primary node.** Forward it to the element a caller would reasonably focus or measure, and treat its *kind* as contract. Where callers only need to focus or scroll, a narrow handle exposing just those actions promises less than the raw node. 4. **Named style override points.** Expose documented parts and override values instead of letting callers target internal structure; internal names are then free to change. 5. **One explicit, searchable escape hatch** for the rest, named so that code search and lint rules can find every use. ## Measure the hatches Escape-hatch usage is the best available signal of what the API is missing. With thirty consuming codebases, a periodic search for passthrough attributes, host-reference uses and override points answers questions no survey can: - a hatch used the same way by many teams is a **missing feature** — build it properly and retire the workaround; - a hatch used by one team for one screen is probably fine where it is; - a hatch used to undo the library's accessibility behaviour is a **defect report** in disguise. ## What shifts the balance - **Number and diversity of consumers.** Thirty teams with different products need more hatches than three teams building one app. - **Who the consumers are.** A library for experienced front-end teams can expose more than one used by occasional contributors. - **Accessibility risk.** Hatches that can overwrite roles, names or focus behaviour deserve the tightest control. - **Upgrade pressure.** A library that must ship breaking changes often benefits from a smaller exposed surface. ## The same trade-off on native platforms Native component libraries face the identical choice: whether callers can reach the underlying platform view, add arbitrary modifiers, or only use declared options. The curated answer — documented extension points, a narrow handle, measured usage — carries over unchanged.

  • In a shared component library, how should passthrough handle an accessibility attribute the library already sets on the element?
    Decide explicitly and document it. Common practice lets callers extend descriptions but guards the role and the focus behaviour, because overwriting those can break the component's accessibility contract. Whatever the rule, it must be the same for every component, and development warnings should flag overrides of guarded attributes.
  • What does a library gain by forwarding a narrow focus-and-scroll handle instead of the raw host node?
    It promises less. With the raw node, callers can depend on its type, children and position in the structure, so any restructuring can break them. A narrow handle exposes only the actions callers need, leaving the library free to change the element behind it.
  • Thirty teams all pass the same style override to widen a component; what should the library team do?
    Treat it as a missing feature. Add a supported option, such as a width or layout property, document it, and help teams move off the override. Leaving thirty copies of the same workaround in place means thirty places that break when the component's internals change.

saying these in an interview costs you the question

  • A fully closed API means teams will simply stop customising
  • Passing every unknown attribute to every element is the safest default
  • Host references are internal details, so forwarding one commits the library to nothing
  • Escape-hatch usage is purely the consumer's business and not worth measuring
  • Style overrides that target internal structure are fine because callers accept the risk