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.

TypeScript Button mit Variant-Prop
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:

TypeScript Layout-Hülle
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:

TypeScript SplitPanel.jsx
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.

TypeScript ProductList (presentational)
function ProductList({ products }) {
    return (
        <ul>
            {products.map((p) => (
                <li key={p.id}>{p.name}</li>
            ))}
        </ul>
    );
}
TypeScript ProductListContainer
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.

Ein häufiger Use Case zeigt mehrere Composition-Patterns in einer Komponente:

TypeScript Modal.jsx
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>
    );
}
TypeScript Verwendung
<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.

Interessantes

React-Komponenten haben keinen sinnvollen 'extends'-Mechanismus.

Eine React-Komponente ist eine Funktion, die JSX zurückgibt — keine Klasse mit überschreibbaren Methoden. Vererbung würde nur Markup, nicht Verhalten teilen, und Markup teilt man besser über JSX-Komposition.

Komposition über children ist der zentrale Strukturmechanismus.

Eine Hülle (Layout, Card, Modal) weiß nicht, was sie umschließt — der Inhalt kommt vom Aufrufer. Das macht Komponenten universell wiederverwendbar, ohne dass die innere Struktur die äußere kennen muss.

Mehrere Slots: ReactNode als Prop statt children-Array-Magie.

Wenn eine Komponente mehrere Inhaltsbereiche hat (header, footer, sidebar), übergibt man sie als separate Props vom Typ ReactNode. Klarer und expliziter als Versuche, mehrere children über Position oder Sub-Komponenten zu identifizieren.

Custom Hooks haben HOCs als Logik-Teilungs-Mechanismus abgelöst.

Vor Hooks (ES2015-2018) waren Higher-Order Components der Weg, Logik wiederzuverwenden. Mit Hooks ist useFoo() in der Komponente direkt aufrufbar — kein Verschachteln nötig, kein „Wrapper-Hell".

Container/Presentational ist heute weniger streng.

Vor Hooks musste man Daten-Logik in einer Container-Komponente kapseln, weil State nur in Class-Components ging. Heute kann jede Function-Component direkt Hooks nutzen — die Trennung ist optional und nur sinnvoll, wenn die Presentational-Komponente in mehreren Kontexten wiederverwendet wird.

Spezialisierte Wrapper sind dünne Komponenten, keine Hierarchien.

Statt class PrimaryButton extends Button: const PrimaryButton = (p) => <Button variant="primary" {...p} />. Eine Zeile, klare Komposition, keine ABI-Vererbungsregeln.

Render Props sind heute selten — Hooks lösen die meisten Use-Cases.

<Foo render={data => <Bar data={data} />} /> war Pre-Hooks-Pattern, um Daten an children weiterzugeben. Heute liefert ein useFoo() dasselbe direkter.

Composition skaliert besser als Vererbung in großen Codebases.

Vererbungshierarchien sind starr — jede neue Variante braucht eine neue Subklasse. Komposition ist flexibel: neue Wrapper-Komponenten kombinieren bestehende. Genau deshalb hat die React-Community Vererbung praktisch vollständig aufgegeben.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Components

Zur Übersicht