React 7,897 has run at Meta across thousands of components without a single component inheritance hierarchy, and the official guidance has never wavered: reuse code by composing components, not by extending them. Even in the class era, extends React.Component was plumbing, not a base class to build a family tree on.
The reason is not dogma. Inheritance forces a parent to anticipate every variation a child might want and to encode each as a prop it must know about. Composition lets the parent stay ignorant: it declares a hole, and the caller fills it with anything React can render.
function RigidCard({ title, body, footerText }) { // every hole must be a string
return <div className="c"><h4>{title}</h4><p>{body}</p><small>{footerText}</small></div>;
}
function Card({ title, children, footer }) { // holes accept any node
return <div className="c"><h4>{title}</h4>{children}<p>{footer}</p></div>;
}
const App = () => (
<div className="row">
<RigidCard title="Rigid" body="Strings only." footerText="A button cannot go here." />
<Card title="Composed" footer={<Counter />}>
<p>Any markup fits, <b>including state</b>.</p>
</Card>
</div>
);RigidCard needs a new prop, and a new branch inside it, for every kind of footer anyone ever wants. Card needed nothing: Counter is a stateful component the card has never heard of, and clicking its button re-renders the counter without touching the card. If a component needs non-visual behavior from another, export it as a plain module and import it from both; if you want a shared base class holding lifecycle logic, that is what custom Hooks (Custom Hooks) are for.