Composition over Inheritance
Reuse code by composing components, not by extending them.
React deliberately favors composition over inheritance. You almost never need to extend a component class; instead you build behavior by combining components and passing props and children.
Ways to reuse
- Pass content via children or named props (slots).
- Share logic with custom hooks.
- Wrap components to add behavior, rather than subclassing.
This keeps relationships explicit and components flexible.
Example
// Compose behavior instead of extending
function Dialog({ title, children, footer }) {
return (
<div className="dialog">
<h2>{title}</h2>
<div>{children}</div>
<div className="footer">{footer}</div>
</div>
);
}
function ConfirmDialog() {
return (
<Dialog title="Confirm" footer={<button>OK</button>}>
<p>Are you sure?</p>
</Dialog>
);
}When to use it
- A button variant is built by wrapping the base Button with extra props rather than extending a class, so the variant gets all base behaviour for free via composition.
- A layout library provides a Box component that accepts children so every page section reuses the same padding and breakpoint logic without a shared base class.
- An analytics higher-order component wraps any component to inject click tracking without modifying the original component's source.
More examples
Specialise via composition not inheritance
Creates a specialised DangerButton by composing Button with a variant prop rather than extending a class.
function Button({ variant = 'default', children, ...rest }) {
return (
<button className={`btn btn--${variant}`} {...rest}>
{children}
</button>
);
}
// DangerButton is Button with a variant, not a class extension
function DangerButton(props) {
return <Button variant="danger" {...props} />;
}Generic container with children
Provides a generic Panel shell via children so any content can be wrapped without subclassing.
function Panel({ title, children, className = '' }) {
return (
<section className={`panel ${className}`.trim()}>
<header className="panel__header"><h2>{title}</h2></header>
<div className="panel__body">{children}</div>
</section>
);
}
// Reuse for stats, alerts, forms β anything
// <Panel title="Revenue"><RevenueChart /></Panel>Named slot props
Uses named prop slots for each region so callers can inject any component into each part of the layout.
function AppLayout({ sidebar, main, footer }) {
return (
<div className="app-layout">
<aside className="app-layout__sidebar">{sidebar}</aside>
<main className="app-layout__main">{main}</main>
<footer className="app-layout__footer">{footer}</footer>
</div>
);
}
// Usage
// <AppLayout
// sidebar={<NavMenu />}
// main={<DashboardContent />}
// footer={<FooterLinks />}
// />
Discussion