Skip to the solution
MediumFrontend7 min read

Explain the concept of 'composition' in React components.

969 views

The problem

Question presented to candidate: "React has no component inheritance. So how do you share and extend behaviour β€” and what does 'composition over inheritance' mean in practice here?"

What a strong answer should cover:

  • Composition means building complex UI by combining components rather than extending them. React has no inheritance model at all, deliberately.
  • The mechanisms: children for arbitrary content, props as slots (passing elements, not just data), render props, and custom Hooks for shared behaviour.
  • The distinction that matters: composition shares structure; custom Hooks share behaviour.
  • Containment (a generic wrapper that does not know its content) versus specialisation (a specific component configuring a generic one).
  • A measurable benefit: a child passed as children is created by the grandparent, so its element is the same object across the parent's re-renders β€” React bails out of re-rendering it, with no memo needed.
  • This makes composition a legitimate performance technique, not just an organisational one.
  • Why inheritance is avoided: it couples components to a hierarchy, and UI variation is rarely a clean single-axis taxonomy.

Clarifying questions expected:

  • "Are we sharing markup/structure, or behaviour?" β€” that picks composition versus a custom Hook.

Code / implementation expected: Yes β€” a wrapper taking children, ideally showing the re-render benefit.

  • composition
  • components
  • design patterns
  • reusability

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 8 sections

Try it in the playground

Open any example in the playground to run and change it.

ReactComposition cuts re-renders without memo β€” watch the counters
import { useState } from "react";

let insideRenders = 0;
let outsideRenders = 0;

// A deliberately "expensive" child. It is identical in both arrangements β€”
// the only difference is WHERE its element gets created.
function Expensive({ tag, count }) {
  // simulate real work
  let x = 0;
  for (let i = 0; i < 200000; i++) x += i;
  return (
    <p style={{ background: "#f6f6f6", padding: 8, borderRadius: 6 }}>
      {tag} rendered <strong>{count}</strong> time(s)
    </p>
  );
}

// (a) The child is created INSIDE the stateful component, so a new element
//     object is produced on every render and React re-renders the subtree.
function Inside() {
  const [n, setN] = useState(0);
  insideRenders++;
  return (
    <div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, marginBottom: 12 }}>
      <h4 style={{ marginTop: 0 }}>Child created inside the stateful parent</h4>
      <button onClick={() => setN(n + 1)}>clicked {n} times</button>
      <Expensive tag="Inside" count={insideRenders} />
    </div>
  );
}

// (b) The stateful component receives the child as its children prop. That element was
//     created by Outside, which never re-renders β€” so it is the SAME object
//     every time and React bails out of re-rendering it.
function Wrapper({ children }) {
  const [n, setN] = useState(0);
  return (
    <div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12 }}>
      <h4 style={{ marginTop: 0 }}>Child passed in as children</h4>
      <button onClick={() => setN(n + 1)}>clicked {n} times</button>
      {children}
    </div>
  );
}

function Outside() {
  outsideRenders++;
  return (
    <Wrapper>
      <Expensive tag="Children" count={outsideRenders} />
    </Wrapper>
  );
}

export default function App() {
  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6 }}>
      <Inside />
      <Outside />
      <p style={{ color: "#666", fontSize: 13 }}>
        Click both buttons a few times. The first counter climbs on every click;
        the second stays at 1. No React.memo anywhere β€” only a change in where
        the element is created.
      </p>
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 39 of 119 in React.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All React.js questions
Up nextMedium

How does React handle events?

Continue