Render Props vs. Children-Function-Pattern: Heute noch relevant?
AI generated
{ }
React 19 · Patterns · API-Design
Render Props vs. Children-Function-Pattern
Heute noch relevant?

Bevor es Hooks gab, waren Render Props und Children-as-Function die Antwort auf Logik-Wiederverwendung zwischen Komponenten. Ein ehrlicher Blick darauf, wo sie im Hooks-Zeitalter noch echten Mehrwert bieten und wo Custom Hooks klar überlegen sind.

14 Min. Lesezeit Component Patterns API-Design Legacy vs. Modern

1. Was Render Props ursprünglich lösen sollten

Vor der Einführung von Hooks in React 16.8 gab es kaum Möglichkeiten, zustandsbehaftete Logik zwischen Komponenten zu teilen, ohne entweder Vererbung oder Higher-Order-Components zu nutzen. Das Render-Props-Pattern löste dieses Problem, indem eine Komponente statt statischer Kindelemente eine Funktion als Prop entgegennahm, die von der Komponente selbst mit internem State oder berechneten Werten aufgerufen wurde. Die aufrufende Seite entschied dann frei, wie diese Werte gerendert werden.

Eine typische MouseTracker-Komponente etwa verfolgte intern die Mausposition und rief eine render-Prop mit den aktuellen Koordinaten auf. Jede Komponente, die diese Logik nutzen wollte, musste sie nicht selbst implementieren, sondern konnte sich einfach als Consumer an den Render-Prop-Vertrag halten. Das war zu seiner Zeit ein echter Fortschritt gegenüber Vererbungsketten und unübersichtlichen HOC-Verschachtelungen.


class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({ x: event.clientX, y: event.clientY });
  };

  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// Nutzung
<MouseTracker render={({ x, y }) => (
  <p>Maus bei ({x}, {y})</p>
)} />

2. Children-as-Function als Variante

Eine eng verwandte Variante nutzt children selbst als Funktion statt eines separaten render-Props. Statt <MouseTracker render={fn} /> schreibt man <MouseTracker>{fn}</MouseTracker>, wobei React children einfach als beliebigen Wert behandelt, auch als Funktion. Der praktische Unterschied ist gering, aber die Lesbarkeit im JSX wirkt für viele natürlicher, weil sich die Struktur stärker an gewöhnlicher Komponentenverschachtelung orientiert.

Beide Varianten teilen dieselbe Grundidee: Eine Komponente kapselt Logik und Zustand, überlässt aber die Darstellung vollständig der aufrufenden Seite. Das Pattern wird deshalb oft als 'Inversion of Control' beschrieben, weil die Kontrolle darüber, was gerendert wird, von der Logik-Komponente an den Aufrufer übergeben wird.


function MouseTracker({ children }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  return (
    <div onMouseMove={(e) => setPosition({ x: e.clientX, y: e.clientY })}>
      {children(position)}
    </div>
  );
}

// Nutzung
<MouseTracker>
  {({ x, y }) => <p>Maus bei ({x}, {y})</p>}
</MouseTracker>

3. Was Hooks an der Ausgangslage verändert haben

Mit Custom Hooks lässt sich dieselbe Logik-Wiederverwendung erreichen, ohne dafür eine zusätzliche Wrapper-Komponente im Baum einzuführen. Ein useMousePosition-Hook kapselt exakt dieselbe Zustandslogik wie die MouseTracker-Komponente, gibt die Werte aber direkt als Rückgabewert zurück, ohne dass eine Komponente ihre eigene Render-Logik in eine fremde Funktion auslagern muss.

Der entscheidende Unterschied ist strukturell: Render Props erzeugen zwangsläufig eine zusätzliche Komponentenebene im Baum, mit eigenem Lifecycle und eigenem Platz in den React DevTools. Ein Custom Hook dagegen fügt sich nahtlos in die aufrufende Komponente ein, es entsteht keine zusätzliche Verschachtelung, und der Code liest sich linear statt funktional verschachtelt.


function useMousePosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMove = (e) => setPosition({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handleMove);
    return () => window.removeEventListener('mousemove', handleMove);
  }, []);

  return position;
}

// Nutzung -- keine Wrapper-Komponente noetig
function Cursor() {
  const { x, y } = useMousePosition();
  return <p>Maus bei ({x}, {y})</p>;
}

4. Das Wrapper-Hell-Problem bei verschachtelten Render Props

Sobald mehrere Render-Props-Komponenten kombiniert werden müssen, etwa Mausposition, Fenstergröße und Authentifizierungsstatus gleichzeitig, entsteht schnell tief verschachteltes JSX, umgangssprachlich als 'Wrapper Hell' oder 'Callback Hell' bezeichnet. Jede zusätzliche Logik-Quelle fügt eine weitere Verschachtelungsebene hinzu, und der eigentliche Rendering-Code rutscht immer weiter nach rechts.

Mit Custom Hooks entfällt dieses Problem vollständig, weil mehrere Hooks einfach nacheinander in derselben Komponente aufgerufen werden, ohne dass sich daraus zusätzliche Verschachtelung ergibt. Das ist einer der stärksten praktischen Gründe, warum sich Custom Hooks für Logik-Wiederverwendung im eigenen Anwendungscode durchgesetzt haben.


// Render-Props-Variante: Wrapper Hell
<MouseTracker>
  {(mouse) => (
    <WindowSize>
      {(size) => (
        <Auth>
          {(user) => (
            <Dashboard mouse={mouse} size={size} user={user} />
          )}
        </Auth>
      )}
    </WindowSize>
  )}
</MouseTracker>

// Hooks-Variante: flach, linear
function Dashboard() {
  const mouse = useMousePosition();
  const size = useWindowSize();
  const user = useAuth();
  return <DashboardView mouse={mouse} size={size} user={user} />;
}

5. Wo Render Props noch echten Sinn ergeben

Ein Bereich, in dem das Pattern nicht obsolet geworden ist, sind Bibliotheks-APIs, bei denen die Darstellung eines UI-Elements bewusst der konsumierenden Anwendung überlassen werden soll, während die Bibliothek selbst nur Verhalten und Zustand steuert. Bibliotheken für Datenlisten mit Drag-and-Drop, virtualisierte Listen oder Formular-Felder nutzen Render Props häufig genau deshalb, weil ein Custom Hook allein nicht ausreicht, um zusätzlich auch die DOM-Struktur oder Event-Handler-Bindung an einer bestimmten Stelle im Baum zu steuern.

Der Grund liegt darin, dass ein Hook nur Werte zurückgeben kann, aber keine Kontrolle über die JSX-Struktur an sich hat. Eine Render-Prop-Komponente kann dagegen zusätzlich steuern, wo genau im Baum eine Interaktion registriert wird, etwa onMouseDown auf einem bestimmten Wrapper-Element, was bei reinen Hooks nicht möglich ist, ohne dass der Aufrufer das selbst manuell verdrahtet.

6. Praxisbeispiel: Headless-Komponenten mit Render Props

Ein konkretes Beispiel ist eine Komponente für sortierbare Tabellen, bei der die Bibliothek Sortierlogik und Zustand verwaltet, aber Header-Zellen und Zeilen-Rendering vollständig der Anwendung überlässt. Der Render-Prop-Vertrag stellt dabei sicher, dass Klick-Handler und Sortierstatus korrekt an die richtigen DOM-Elemente gebunden werden, während die visuelle Darstellung frei bleibt.

Solche 'Headless Components' sind ein legitimer und in der Praxis weit verbreiteter Anwendungsfall für Render Props, weil die Trennung zwischen Verhalten und Darstellung genau der Kernidee des Patterns entspricht. Bibliotheken wie Downshift für Autocomplete-Verhalten nutzten historisch genau dieses Muster, wenngleich viele davon mittlerweile parallel auch Hook-basierte APIs anbieten.


function SortableTable({ data, renderHeader, renderRow }) {
  const [sortKey, setSortKey] = useState(null);
  const sorted = useMemo(() => sortData(data, sortKey), [data, sortKey]);

  return (
    <table>
      <thead>{renderHeader({ sortKey, onSort: setSortKey })}</thead>
      <tbody>{sorted.map((row) => renderRow(row))}</tbody>
    </table>
  );
}

7. Hybride APIs: Custom Hook und Render Prop parallel anbieten

Viele etablierte Bibliotheken lösen die Abwägung nicht endgültig zugunsten eines der beiden Pattern, sondern bieten beide Zugänge parallel an. Intern kapselt ein Custom Hook die eigentliche Zustandslogik, und eine dünne Render-Props- oder Children-Function-Komponente wird lediglich als Wrapper um genau diesen Hook herum gebaut. So profitieren Nutzer, die reinen Wertezugriff brauchen, vom schlanken Hook, während Nutzer, die zusätzlich DOM-Kontrolle benötigen, die Wrapper-Komponente einsetzen können, ohne dass die Bibliothek die Logik zweimal pflegen muss.

Dieses Muster ist besonders in Migrationsphasen sinnvoll, wenn eine Bibliothek historisch mit Render Props aufgebaut wurde und bestehende Konsumenten nicht sofort umgestellt werden sollen. Der Hook wird zur eigentlichen Quelle der Wahrheit, die Render-Props-Komponente bleibt als dünne, rückwärtskompatible Hülle bestehen, was Wartungsaufwand spart und beiden Zielgruppen gerecht wird.


// Hook ist die eigentliche Logik-Quelle
function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = useCallback(() => setValue((v) => !v), []);
  return [value, toggle];
}

// Render-Props-Komponente als duenner Wrapper um den Hook
function Toggle({ children, initial }) {
  const [value, toggle] = useToggle(initial);
  return children({ value, toggle });
}

8. Wann Custom Hooks klar die bessere Wahl sind

Für den überwiegenden Teil der Logik-Wiederverwendung im eigenen Anwendungscode, etwa Datenfetching, Formular-State, Media-Queries oder Debouncing, ist ein Custom Hook nahezu immer die bessere Wahl. Er ist einfacher zu testen, weil er ohne Rendering-Kontext isoliert per renderHook geprüft werden kann, erzeugt keinen zusätzlichen Komponentenbaum und lässt sich problemlos mit anderen Hooks kombinieren.

Auch aus TypeScript-Sicht sind Hooks angenehmer: Der Rückgabetyp eines Hooks lässt sich präzise typisieren, während bei Render Props zusätzlich die Signatur der Callback-Funktion typisiert werden muss, was in der Praxis zu mehr Typdefinitionen für denselben Nutzen führt. Sobald die Frage lautet, ob die konsumierende Seite auch Kontrolle über die konkrete DOM-Struktur an mehreren Stellen braucht, kippt die Abwägung wieder Richtung Render Props oder Headless-Component-Pattern.

9. Entscheidungshilfe für die eigene Codebasis

Die pragmatische Faustregel lautet: Wenn ausschließlich Werte oder Zustand geteilt werden sollen, ist ein Custom Hook die richtige Wahl. Wenn zusätzlich Kontrolle über bestimmte DOM-Bindungen, Event-Handler-Platzierung oder eine öffentliche, framework-unabhängige Bibliotheks-API benötigt wird, bleibt das Render-Props- oder Children-Function-Pattern eine valide, bewusste Designentscheidung.

Wichtig ist, das Pattern nicht aus Gewohnheit im eigenen Anwendungscode fortzuführen, nur weil es in einer älteren Codebasis so etabliert war. Ein Refactoring zu Custom Hooks lohnt sich in den meisten Fällen, sobald die Render-Prop-Komponente ausschließlich internen Anwendungscode bedient und keine öffentliche, konsumierende API darstellt.

Kriterium Render Props / Children-Function Custom Hooks
Zusätzliche Komponentenebene im Baum ja, immer nein
Kombinierbarkeit mehrerer Logik-Quellen verschachtelt (Wrapper Hell) flach, linear
Kontrolle über DOM-Struktur/Event-Bindung ja, an der Aufruferseite steuerbar nur eingeschränkt möglich
Isoliertes Testen ohne Rendering schwieriger einfach per renderHook
Typische Anwendung heute Headless-Component-Bibliotheken Anwendungsinterner Logik-Reuse

Mironsoft

React-Architektur, Performance und Magento-Frontend-Integration

React-Frontends, die schnell bleiben statt mit jedem Feature langsamer zu werden?

Wir prüfen bestehende React-Anwendungen auf unnötige Re-Renders, aufgeblähte Bundles und fragile State-Verwaltung und bauen daraus ein Frontend, das performant bleibt und sich sauber an Magento oder andere Backends anbindet.

Performance-Audit

Re-Renders, Bundle-Größe und Ladezeiten systematisch messen und beheben.

State-Architektur

Context, Zustand und Server State sauber trennen statt alles in einen Topf zu werfen.

Magento-Integration

GraphQL- oder REST-Anbindung an Magento robust und typsicher aufbauen.

10. Zusammenfassung

Render Props: Das Wichtigste auf einen Blick

Historischer Zweck

Render Props lösten Logik-Wiederverwendung vor der Einführung von Hooks.

Größte Schwäche

Verschachtelung mehrerer Render-Props-Komponenten führt zu Wrapper Hell.

Moderne Alternative

Custom Hooks für reinen State- und Logik-Reuse ohne DOM-Kontrolle.

Verbleibender Einsatzbereich

Headless-Component-Bibliotheken mit Bedarf an DOM-Struktur-Kontrolle.

11. FAQ: Render Props: Das Wichtigste auf einen Blick

1Ist das Render-Props-Pattern in React 19 veraltet?
Nicht generell veraltet, aber für den Großteil der Anwendungsfälle durch Custom Hooks abgelöst. Es bleibt relevant für Bibliotheks-APIs, bei denen die konsumierende Seite Kontrolle über die DOM-Struktur benötigt.
2Was ist der Unterschied zwischen Render Props und Children-as-Function?
Funktional identisch, der Unterschied ist rein syntaktisch. Bei Render Props wird eine separate Prop wie render übergeben, bei Children-as-Function wird children selbst als Funktion genutzt.
3Warum entsteht bei verschachtelten Render Props Wrapper Hell?
Weil jede zusätzliche Logik-Quelle eine weitere Komponentenebene mit eigener Callback-Funktion erfordert. Mehrere kombinierte Render-Props-Komponenten verschachteln sich dadurch tief ineinander, was JSX schwer lesbar macht.
4Kann ein Custom Hook Render Props in jedem Fall ersetzen?
Nein. Ein Hook kann nur Werte zurückgeben, aber keine Kontrolle über die konkrete DOM-Struktur oder Event-Handler-Platzierung an mehreren Stellen im Baum übernehmen, was bei Headless-Component-Bibliotheken oft nötig ist.
5Was sind Headless Components und was haben sie mit Render Props zu tun?
Headless Components trennen Verhalten und Zustand von der visuellen Darstellung vollständig. Render Props sind eines der klassischen Werkzeuge, um diese Trennung technisch umzusetzen, da sie der Aufruferseite die volle Kontrolle über das Rendering überlassen.
6Sollte ich bestehenden Render-Props-Code in meiner Anwendung refactoren?
In den meisten Fällen ja, sofern die Komponente nur internen Anwendungscode bedient. Sobald sie eine öffentliche Bibliotheks-API darstellt, die DOM-Kontrolle an die konsumierende Seite abgibt, ist ein Refactoring nicht zwingend nötig.
7Warum sind Custom Hooks einfacher zu testen als Render Props?
Custom Hooks lassen sich mit renderHook isoliert testen, ohne dass eine vollständige Komponente gerendert werden muss. Render Props erfordern das Rendern der Wrapper-Komponente inklusive der Callback-Funktion, was den Testaufbau komplexer macht.
8Erzeugt eine Render-Props-Komponente wirklich messbaren Overhead?
Der reine Rendering-Overhead ist meist gering, das eigentliche Problem ist strukturelle Komplexität durch zusätzliche Komponentenebenen im Baum, die Debugging und Lesbarkeit erschweren, nicht in erster Linie die Performance.
9Gibt es Bibliotheken, die sowohl Render Props als auch Hooks parallel anbieten?
Ja, viele etablierte UI- und Verhaltens-Bibliotheken bieten mittlerweile beide APIs parallel an, um sowohl bestehende Render-Props-Nutzer als auch neue, Hook-basierte Codebasen zu unterstützen.
10Wie typisiere ich eine Render-Prop-Komponente sauber in TypeScript?
Man definiert einen generischen Typ für die Callback-Funktion, die als Prop übergeben wird, inklusive Parametertyp und Rückgabetyp. Das ist möglich, erfordert aber mehr Typdefinitionsaufwand als ein vergleichbarer Custom Hook mit typisiertem Rückgabewert.