Q173: Component Composition, Compound Components and Slot APIs
What Interviewers Want To Evaluate
This question checks whether you can design reusable React APIs that remain flexible. Interviewers want composition, children, compound components, controlled vs uncontrolled component APIs, slots, render props, context inside component families, accessibility contracts, and avoiding prop explosion.
Senior answers should explain API ergonomics and long-term maintainability.
Short Interview Answer
React composition means building components that accept other components or elements as children instead of hard-coding every variation. Compound components expose a family of related pieces, like Tabs.Root, Tabs.List, Tabs.Trigger, and Tabs.Content, that coordinate through shared state or context. Slot APIs let consumers replace specific internal regions while preserving behavior. These patterns reduce prop explosion and make reusable components flexible, but they need clear ownership, accessibility, and constraints.
Detailed Interview Answer
Composition is one of React's strongest design tools.
Instead of this:
<Card title="Billing" footerText="Save" showIcon />
Prefer a composable shape when variation grows.
<Card>
<Card.Header>Billing</Card.Header>
<Card.Body>Plan details</Card.Body>
<Card.Footer>
<Button>Save</Button>
</Card.Footer>
</Card>
The consumer controls structure while the component provides consistent styling and behavior.
Prop Explosion
Prop explosion happens when a component grows a prop for every variation.
showIcon
iconPosition
footerMode
compactHeader
showCloseButton
customActions
actionAlign
This makes the component hard to learn and harder to change.
Composition can move variation into children.
Compound Components
Compound components are related components designed to work together.
<Tabs.Root defaultValue="profile">
<Tabs.List>
<Tabs.Trigger value="profile">Profile</Tabs.Trigger>
<Tabs.Trigger value="billing">Billing</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="profile">Profile form</Tabs.Content>
<Tabs.Content value="billing">Billing form</Tabs.Content>
</Tabs.Root>
The API reflects the domain.
Internally, the pieces may share state through Context.
Controlled and Uncontrolled APIs
Reusable components often support both.
<Tabs.Root value={tab} onValueChange={setTab} />
Controlled mode lets the parent own state.
<Tabs.Root defaultValue="profile" />
Uncontrolled mode lets the component own state.
Supporting both requires careful rules about which props are authoritative.
Slot APIs
A slot is a named region that can be replaced.
<Dialog
trigger={<Button>Open</Button>}
footer={<DialogActions />}
/>
or:
<Dialog.Root>
<Dialog.Trigger asChild>
<Button>Open</Button>
</Dialog.Trigger>
<Dialog.Content>...</Dialog.Content>
</Dialog.Root>
Slots are useful when layout is fixed but content varies.
asChild Pattern
The asChild pattern lets a component pass behavior to a child element.
This can avoid wrapper elements.
It is useful for buttons, links, menu triggers, and dialog triggers.
It must preserve refs, event handlers, accessibility attributes, and keyboard behavior.
Render Props
Render props expose state to a child function.
<Toggle>
{({ on, toggle }) => (
<button onClick={toggle}>{on ? "On" : "Off"}</button>
)}
</Toggle>
They are flexible but can be noisier than hooks or compound components.
Accessibility Contracts
Reusable components must own accessibility where possible.
Tabs should manage:
roles
aria-selected
aria-controls
keyboard navigation
focus movement
disabled states
orientation
If consumers compose pieces incorrectly, the library should fail clearly or constrain the API.
Component API Design Questions
Before creating an abstraction, ask:
What variation is actually needed?
Who owns state?
What is controlled externally?
What structure must be enforced?
What can be slotted?
What accessibility behavior is required?
What escape hatches are safe?
What should remain product-specific?
Common Mistakes
A common mistake is creating a mega component with dozens of props.
Another mistake is making compound components that depend on hidden child order.
Another mistake is exposing slots without preserving accessibility.
Another mistake is supporting controlled and uncontrolled modes without clear precedence.
Another mistake is abstracting before patterns repeat.
Senior Trade-Offs
Composition increases flexibility, but unconstrained flexibility can make a design system inconsistent.
Good component APIs provide a paved path, strong defaults, and safe escape hatches.
They do not try to solve every future use case in the first version.
Debugging Workflow
When a reusable component API becomes painful:
list real usage examples
identify prop explosion
separate layout, behavior, and content variation
check controlled/uncontrolled ownership
review accessibility behavior
remove unused flexibility
document supported composition
add tests for keyboard and state behavior
Interview Framing
Explain composition, compound components, slots, controlled APIs, accessibility, and trade-offs around flexibility.
Revision Notes
Component architecture is API design. The caller experience matters as much as the internal implementation.
Key Takeaways
Use composition to make variation natural, but keep behavior and accessibility contracts clear.
Follow-Up Questions
- What is prop explosion?
- What are compound components?
- When are slots useful?
- What does controlled mode mean?
- What does uncontrolled mode mean?
- Why is
asChilduseful? - What are render props?
- How should reusable components handle accessibility?
- What makes an escape hatch safe?
- When should you avoid abstraction?
How I Would Answer This In A Real Interview
I would say composition lets reusable components support variation without endless props. I would use compound components for related UI families, slot APIs for replaceable regions, controlled and uncontrolled APIs where useful, and strong accessibility contracts so flexibility does not break behavior.