Composition vs inheritance in React
Composition vs Inheritance
React strongly favors composition over inheritance. Instead of extending components through class hierarchies, you compose them by combining smaller components together.
Composition with children
tsx
// A generic container component
function Card({ children, title }: { children: React.ReactNode; title: string }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">{children}</div>
</div>
);
}
// Compose different cards
function App() {
return (
<>
<Card title="Profile">
<Avatar />
<UserInfo />
</Card>
<Card title="Settings">
<SettingsForm />
</Card>
</>
);
}Specialization Pattern
Create specialized versions of a generic component:
tsx
// Generic Dialog
function Dialog({ title, message, children }: {
title: string;
message: string;
children?: React.ReactNode;
}) {
return (
<div className="dialog">
<h1>{title}</h1>
<p>{message}</p>
{children}
</div>
);
}
// Specialized dialogs via composition
function WelcomeDialog() {
return (
<Dialog
title="Welcome!"
message="Thanks for joining our platform."
/>
);
}
function DeleteConfirmDialog({ onConfirm }: { onConfirm: () => void }) {
return (
<Dialog title="Delete?" message="This action cannot be undone.">
<button onClick={onConfirm}>Yes, delete</button>
</Dialog>
);
}Slot Pattern (Named Composition)
tsx
function Layout({
header,
sidebar,
content,
footer
}: {
header: React.ReactNode;
sidebar: React.ReactNode;
content: React.ReactNode;
footer: React.ReactNode;
}) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{content}</main>
<footer>{footer}</footer>
</div>
);
}
// Use named slots
<Layout
header={<Navbar />}
sidebar={<Sidebar />}
content={<MainContent />}
footer={<Footer />}
/>Why NOT Inheritance?
tsx
// ❌ Inheritance — DON'T do this in React
class Button extends React.Component { /* ... */ }
class PrimaryButton extends Button { /* ... */ }
class DangerButton extends PrimaryButton { /* ... */ }
// Rigid hierarchy, hard to change
// ✅ Composition — DO this instead
function Button({ variant, children, ...props }: ButtonProps) {
return <button className={`btn btn-${variant}`} {...props}>{children}</button>;
}
<Button variant="primary">Save</Button>
<Button variant="danger">Delete</Button>Composition Patterns Summary
| Pattern | How | Use Case |
|---|---|---|
| children | Pass JSX as children | Generic containers, wrappers |
| Specialization | Pre-fill props of generic component | Specific dialog, button variants |
| Slots | Named props for different areas | Layouts, complex UI |
| Render Props | Pass function as prop | Dynamic rendering logic |
| HOC | Wrap component with another | Cross-cutting concerns |
Important:
React team recommends composition over inheritance in all cases. Use children, named props (slots), and component composition to build flexible UIs. Inheritance creates rigid hierarchies that are hard to refactor — composition keeps components independent and reusable.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.