skip to content

React has no mechanism for one component to extend another the way a subclass extends a base class. Given a generic `Dialog` component, how do you build a `ConfirmDialog` that is always the confirm variant, and what is the general rule React expects you to follow?

level: middleimportance: should knowfreq 52%

answer

  1. no extends between your components
  2. renders-a, not is-a
  3. two shapes: containment and specialization
  4. the specific one configures the generic one
  5. shared logic goes to a hook

basics

~20 s

You write ConfirmDialog as a normal component that renders Dialog with preset props and its own nested content. React reuses code by composition only: containment passes content through children, and specialization wraps a general component in a more specific one.

solid answer

~50 s

You do not extend `Dialog` — you *use* it. `ConfirmDialog` is a plain function component whose body returns `<Dialog title="Confirm" …>` with the confirm-specific buttons nested inside, forwarding through only the props its callers still need to vary. React frames this as two patterns: **containment**, where a generic component renders `{children}` and knows nothing about what it wraps, and **specialization**, where a specific component renders a generic one with particular props. Both are ordinary function calls and JSX nesting — there is no inheritance API at all. Even `class ConfirmDialog extends React.Component` is not inheriting UI from another component; it extends React's own base class to get the class-component contract. Non-visual logic is shared through custom hooks or plain functions rather than a shared ancestor, so there is no base component whose change ripples through a hierarchy.

code

jsx · 18 lines
jsx
function Dialog({ title, children, onClose }) {
  return (
    <div role="dialog" aria-label={title}>
      <h2>{title}</h2>
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}

export function ConfirmDialog({ message, onConfirm, onClose }) {
  return (
    <Dialog title="Confirm" onClose={onClose}>
      <p>{message}</p>
      <button onClick={onConfirm}>Yes</button>
    </Dialog>
  );
}

go deeper

for a junior

Be able to say that React components are composed, never subclassed, and to write a small wrapper component that renders a more generic one with fixed props.

for a middle

Distinguish containment from specialization precisely, and explain why extends React.Component is a framework binding rather than component reuse. Know that shared logic belongs in a custom hook.

for a senior

Show judgment about the wrapper's surface: which props to fix, which to forward, and why spreading everything through makes the underlying component's API your own. Recognise when repeated wrappers mean the generic component's props are wrong.

for a principal

Own the library-level consequence: composition-only reuse means consumers can never depend on your internals, so you keep the freedom to change how a component renders. Set the policy on how much of a base component's API your presets forward.

## There is nothing to extend A React component is a function that takes props and returns elements. There is no protected member to override, no `super.render()`, no template method — and React deliberately never added one. Interviewers ask this because candidates arriving from class-based UI toolkits look for `extends Dialog` and cannot find it. What React offers instead are two shapes, and they are just JSX. ## Containment: the generic component owns the frame A component that renders `{children}` does not know what it contains, and that is the point: ```jsx function Dialog({ title, children, onClose }) { return ( <div role="dialog" aria-label={title}> <h2>{title}</h2> {children} <button onClick={onClose}>Close</button> </div> ); } ``` `Dialog` owns the shell, the focus behaviour and the accessibility wiring. The caller owns the contents. Nothing in `Dialog` needs to change when a new kind of dialog appears — which is exactly the property a base class does not have. ## Specialization: the specific component configures the generic one A "subclass" becomes a wrapper component: ```jsx function ConfirmDialog({ message, onConfirm, onClose }) { return ( <Dialog title="Confirm" onClose={onClose}> <p>{message}</p> <button onClick={onConfirm}>Yes</button> <button onClick={onClose}>Cancel</button> </Dialog> ); } ``` `ConfirmDialog` decides what is fixed (the title, the two buttons) and what stays configurable (the message, the callbacks). Note what it did *not* do: it did not expose every `Dialog` prop. Narrowing the surface is a feature — the specialized component is easier to use precisely because it decides more. When you do want to stay open, spread the rest through explicitly: ```jsx function DangerDialog({ children, ...rest }) { return <Dialog {...rest} tone="danger">{children}</Dialog>; } ``` The cost of that flexibility is that the wrapper's API is now whatever `Dialog`'s is, forever. ## What about `extends React.Component`? This trips people up. A class component does use inheritance — but it inherits from React's own base class to obtain `this.setState`, the lifecycle contract and the `render` protocol. It is a framework binding, not a code-reuse mechanism between your components. `class ConfirmDialog extends Dialog` is not a supported pattern: React does not merge the two `render` methods, and you end up either shadowing the parent entirely or reaching into implementation details that the parent is free to change. In modern React you write function components anyway, and class components are mainly something you read in legacy code or migrate — with error boundaries as the one remaining case they are still required for. ## Sharing behaviour rather than markup If what you want to reuse is not markup but logic — a subscription, some derived state, a keyboard trap — you do not need a component relationship at all. Extract a custom hook or a plain function and call it from both components. Two components that need the same behaviour become two callers of the same hook, not two descendants of a common ancestor. That is why React codebases have wide, flat component trees instead of deep hierarchies. ## Why React made this choice Inheritance couples the child to the parent's internals: the parent cannot change how it renders without auditing everyone who extended it, and behaviour that varies along two independent axes (variant × size, say) forces a combinatorial explosion of subclasses. Composition replaces "is a" with "renders a": `ConfirmDialog` renders `Dialog` the same way any other caller does, through its public props. The general design principle behind this is older and broader than React, but React's version of it is unusually strict — the framework simply provides no alternative. ## How to answer in the interview Say the three things: there is no component inheritance in React; containment (`children`) covers "this component wraps arbitrary content"; specialization (a wrapper that renders the generic one with fixed props) covers "this component is a preset version of that one". Then add the detail that separates a solid answer from a memorized one: shared *logic* goes into a custom hook, not into a shared ancestor.

  • A specialized wrapper needs to stay usable for every prop the generic component accepts. How do you handle that, and what does it cost?
    Spread the remaining props through: `<Dialog {...rest} tone="danger">`. It keeps the wrapper transparent, but it also makes the generic component's entire API part of your wrapper's public contract, so any change there is a change here. Prefer listing the props you intend to support when the wrapper exists precisely to narrow the surface.
  • Two unrelated components need the same escape-key handling. Where does that code live?
    In a shared custom hook that both call, or a plain helper function if it needs no React state. Neither component becomes a parent or a child of the other. Sharing behaviour through a call rather than a hierarchy is what keeps React trees flat and lets a component adopt or drop behaviours independently.
  • Is `class MyThing extends React.Component` a counterexample to "React has no inheritance"?
    No. That inherits React's own base class to get the class-component contract — `this.setState`, lifecycle methods, the `render` protocol. It is a binding to the framework, not a way to reuse another component's UI. Extending one of your own components is unsupported and couples you to its internals.

saying these in an interview costs you the question

  • Suggests class Child extends ParentComponent to reuse UI
  • Calls composition just a stylistic preference over inheritance
  • Thinks extends React.Component reuses another component's markup
  • Copies the generic component's source to specialize it
  • Puts shared behaviour in a base component instead of a hook

context