skip to content

What must a wrapper component forward so that callers of the component it wraps cannot tell the wrapper is there?

level: middleimportance: should knowfreq 48%

answer

  1. it should be invisible to the caller
  2. forward what you do not consume
  3. inputs, events, named regions, element handle
  4. collisions silently shadow the inner input

basics

~20 s

Everything it does not consume: inputs and attributes it does not recognise, the caller's event bindings, projected content in the same named regions, and a handle to the underlying element. The wrapper adds only its own behaviour.

solid answer

~40 s

A wrapper exists to add behaviour around a whole subtree — an access gate, error handling, instrumentation, layout chrome — without the inner component knowing. Transparency is the whole contract, so the wrapper has to pass through the parts it does not own: inputs and attributes it does not consume, event bindings the caller declared, projected content placed into the same named regions, and whatever escape hatch callers use to reach the real element. Three leaks show up in review: a swallowed input, where the caller sets something and nothing happens; lost content regions, where only the default one survives the wrapper; and broken element access. Keep the wrapper's own input names distinct from the wrapped component's, or a collision will silently shadow one of them.

go deeper

for a junior

Know what a wrapper is: a component that renders another component plus something extra. Remember it has to pass along the inputs, events and content it did not ask for itself.

for a middle

List the four things to forward — unconsumed inputs and attributes, event bindings, every named content region, an element handle — and name the symptom each leak produces.

for a senior

Catch those leaks in review, prevent name collisions between the wrapper's API and the wrapped component's, and say when a wrapper should be explicitly opinionated instead of transparent.

for a principal

Set the policy: how many pass-through layers a component may accumulate, who reviews a new one, and what has to be true before wrapping beats publishing a content region or a unit.

## What a wrapper is for A **wrapper** — pass-through component, decorator, higher-order component; the names vary by community — is a component whose job is to render another component and add something around it: an access check, error handling, instrumentation, a layout shell, an adapter that translates one input shape into another. It is the composition answer to needing this component plus this behaviour everywhere. The wrapper's entire value is that callers should not have to care. If using the wrapped component through the wrapper differs from using it directly, every consumer pays for the wrapper's existence. ## The four things a transparent wrapper forwards 1. **Inputs and attributes it does not consume.** The wrapper declares the few it reads and passes the rest down untouched, including plain markup attributes and accessibility attributes, which are exactly what callers reach for. 2. **Event bindings.** A handler the caller attached must reach the inner component's corresponding output. Dropping one produces the worst kind of bug: a control that looks right and reports nothing. 3. **Every content region, by name.** A wrapper that forwards only the default region silently deletes the header and footer the inner component published. 4. **The escape hatch to the real element.** When callers need to focus, measure or scroll the underlying node, the wrapper must hand that handle through — its own markup is not what the caller asked for. ## The three leaks, and how each looks in review | Leak | Symptom | Fix | |---|---|---| | Swallowed input | the caller sets something and nothing happens | forward the unconsumed remainder rather than a hand-written list | | Lost content region | named regions render empty through the wrapper | forward regions by name, including ones added later | | Broken element access | the caller cannot focus or measure the control | hand the inner element's handle through | A quieter fourth is a **name collision**: the wrapper declares an input the inner component also uses. One of them wins, the caller cannot tell which, and adding an input to the inner component can break a wrapper that was correct yesterday. Prefix or namespace the wrapper's own inputs, and keep them few. ## What the projected subtree keeps Content the caller passes *through* a wrapper is still the caller's content: - It was created in the caller's scope and resolves its names there, wherever the wrapper ends up rendering it. - It keeps its identity when the wrapper re-renders for its own reasons, so stateful things inside it — an open menu, a half-typed value, a scroll offset — survive. - Where it sits in the **component tree** decides what it reads from ancestors; that lookup has its own rules and its own topic. So a wrapper is free to re-render, but it is not free to re-parent content casually: moving where the content lands changes what it inherits from the tree around it. ## When a wrapper should stop pretending to be transparent Not every wrapper is a pass-through, and pretending otherwise is worse than being explicit: - A **gate** may render a denial instead of the child. That is a decision, not decoration. - A **boundary** may replace a failed subtree with a recovery surface. - An **adapter** deliberately changes the API: a different input shape, a narrower surface, renamed events. These should be named for the decision they make, keep a small explicit API, and be documented as not invisible. The failure mode to avoid is a wrapper that is transparent in most cases and quietly opinionated in one. ## Practical habits - Forward the **remainder**, never an enumerated list: a list goes stale the first time the inner component gains an input. - Add a test that sets an input the wrapper knows nothing about and asserts it arrived at the inner component. - Keep a wrapper to **one** added behaviour. Two behaviours are two wrappers, or better, one reusable unit and one wrapper. - Count your layers: each one is another contract to keep in sync, another namespace to collide in, and another hop for whoever is reading the tree. ## Where runtimes differ The mechanics of the remainder and of an element handle are per-runtime. Some spread unconsumed inputs automatically onto the root element the wrapper renders, some require an explicit rest-and-spread step, and some make the wrapped component opt in before a handle can be exposed at all. The contract is constant — consume what you use, forward the rest — and only the amount of ceremony changes.

  • The caller's projected content sits inside a wrapper's markup. What changes for that content?
    Where it renders can change, but it is still the caller's content, created in the caller's scope, and it keeps its identity across the wrapper's own re-renders. What it reads from ancestors follows where it ends up in the component tree rather than where it was written, and that lookup is a topic of its own.
  • When is a wrapper the wrong tool even though the behaviour really is shared?
    When nothing about the subtree is involved and only state and effects are shared — then a reusable stateful unit the component calls avoids forcing a tree level on every consumer. Wrapping is for behaviour that genuinely applies to a whole subtree, such as gating it, instrumenting it or recovering it from failure.

saying these in an interview costs you the question

  • Assumes only the inputs the wrapper declares need to reach the inner component
  • Forwards the default content region and silently drops the named ones
  • Reuses the inner component's input names and shadows them by accident
  • Thinks callers can reach the real element without the wrapper forwarding a handle
  • Believes projected content is re-created whenever the wrapper re-renders
  • Piles several unrelated behaviours into one wrapper to save a layer