React empfiehlt seit jeher: Komposition vor Vererbung. Während andere Frameworks oder Klassen-Hierarchien Komponenten oft via extends spezialisieren, baut React auf das Verschachteln und Konfigurieren von Komponenten – über Props und children. Dieser Artikel zeigt, warum das so ist, welche typischen Patterns daraus entstehen und wann eine geteilte „Hilfs-Funktion" tatsächlich besser ist als eine geteilte Komponente.
Vererbung in React – warum eigentlich nicht?
In klassischer OOP würde man eine Button-Klasse haben, davon PrimaryButton extends Button, vielleicht noch IconButton extends PrimaryButton. In React ist diese Struktur ungeeignet, weil:
- React-Komponenten haben keinen sinnvollen Lebenszyklus für Vererbung – sie sind Funktionen, die JSX zurückgeben.
- Beziehungen zwischen Komponenten lassen sich über Props und children sauberer ausdrücken.
- Vererbungsketten werden bei UI-Bestandteilen schnell unübersichtlich und unflexibel: Was, wenn eine Komponente plötzlich aus zwei Eltern erben soll?
Die offizielle Empfehlung der React-Doku lautet seit Jahren: „We haven't found any use cases where we would recommend creating component inheritance hierarchies."
Spezialisierung über Props
Das einfachste Werkzeug für Spezialisierung sind Props. Eine generische Komponente erhält Konfiguration von außen.
function Button({ variant = 'default', children, ...rest }) {
const className = `btn btn-${variant}`;
return (
<button className={className} {...rest}>
{children}
</button>
);
}
// Verwendung
<Button variant="primary">Speichern</Button>
<Button variant="danger">Löschen</Button>Statt class PrimaryButton extends Button gibt es nur eine Komponente, die per Prop konfiguriert wird.
Komposition über children
Die zweite, oft elegantere Form ist die Verschachtelung über children. Eine generische „Hülle" weiß nicht, was sie umschließt:
function PageLayout({ children }) {
return (
<div className="page">
<Header />
<main>{children}</main>
<Footer />
</div>
);
}
// Spezielle Seite – ohne Vererbung
function ProfilePage({ user }) {
return (
<PageLayout>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</PageLayout>
);
}PageLayout und ProfilePage haben keine Vererbungsbeziehung – sie sind unabhängige Komponenten, die ineinander verschachtelt werden.
Mehrere benannte Slots
Wenn eine Komponente mehrere Inhaltsbereiche hat, übergibt man sie als separate Props vom Typ ReactNode. Das ist die React-typische „Slot"-Variante:
function SplitPanel({ left, right }) {
return (
<div className="split">
<aside>{left}</aside>
<section>{right}</section>
</div>
);
}
// Verwendung
<SplitPanel
left={<Sidebar />}
right={<MainContent />}
/>Dieses Vorgehen kommt ohne zusätzliche API aus und ist im Code sofort lesbar.
Container / Presentational
Ein klassisches Komposition-Pattern in React: Eine Presentational-Komponente ist „dumm" und erhält alle Daten als Props. Eine Container-Komponente kümmert sich ums Laden, Filtern, State und gibt die fertigen Daten weiter.
function ProductList({ products }) {
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}function ProductListContainer() {
const { data: products = [] } = useProducts();
return <ProductList products={products} />;
}Vorteil: Die ProductList ist ohne Daten-Layer testbar und in anderen Kontexten wiederverwendbar.
Heute ist die Trennung weniger streng als früher (Hooks haben vieles vereinfacht), das Prinzip „Logik außen, JSX innen" bleibt aber wertvoll. Mehr dazu unter Container / Presentational.
Wann ist Vererbung – fast – nötig?
In den seltenen Fällen, in denen mehrere Komponenten dieselbe Logik (nicht Markup) teilen sollen, gibt es bessere Alternativen als Vererbung:
- Custom Hooks – wiederverwendbare Logik in einer Funktion.
- Higher-Order Components (HOC) – Funktion, die eine Komponente erweitert (heute selten).
- Render Props – Komponente, die ihre Render-Logik delegiert.
- Composition – einfach Komponenten ineinanderstecken.
Custom Hooks lösen heute mit Abstand die meisten Fälle. Vergleiche Einführung Custom Hooks.
Praktisches Beispiel: Modal mit Footer-Slot
Ein häufiger Use Case zeigt mehrere Composition-Patterns in einer Komponente:
function Modal({ open, title, footer, onClose, children }) {
if (!open) return null;
return (
<div className="overlay" onClick={onClose}>
<div className="dialog" onClick={(e) => e.stopPropagation()}>
{title && <h2 className="dialog-title">{title}</h2>}
<div className="dialog-body">{children}</div>
{footer && <div className="dialog-footer">{footer}</div>}
</div>
</div>
);
}<Modal
open={open}
onClose={() => setOpen(false)}
title="Eintrag löschen?"
footer={
<>
<button onClick={() => setOpen(false)}>Abbrechen</button>
<button onClick={handleDelete}>Löschen</button>
</>
}
>
<p>Möchtest du den Eintrag wirklich entfernen?</p>
</Modal>Ein einziges, generisches Modal deckt alle möglichen Dialog-Varianten ab – ohne extends, ohne Vererbung.
Faustregeln
- Erst Komposition versuchen. In den allermeisten Fällen kommst du damit sauber durch.
- Logik in Custom Hooks, Markup in Komponenten.
- Spezialisierte Komponenten als dünne Wrapper, die generischere Komponenten konfigurieren.
- Keine
extends-Hierarchien aufbauen. Hat sich in React-Bestandscode noch nie ausgezahlt.