Slot basierte Komponenten APIs in React
AI generated
</>
{ }
React · Component Design · asChild
Slot basierte Komponenten APIs in React
das asChild Muster verstehen und selbst implementieren

Slot basierte Komponenten APIs ersetzen das Root Element einer Komponente durch genau ein uebergebenes Kindelement, statt ein zusaetzliches DOM Element einzufuegen. Das asChild Muster von Radix hat diesen Ansatz populaer gemacht, doch die zugrunde liegende Slot Komponente laesst sich auch ohne fremde Bibliothek in wenigen Zeilen selbst bauen.

18 Min. Lesezeit asChild · Props Merging · Ref Merging React 19 · TypeScript

1. Was Slot basierte Komponenten APIs loesen

Eine Slot basierte Komponenten API loest ein Problem, das beim as Prop haeufig auftritt: manchmal soll eine Komponente ihr Root Element nicht durch einen Elementnamen ersetzen, sondern durch ein komplettes, bereits vorhandenes Kindelement mit eigenen Props. Der klassische Fall ist ein Button, der optisch wie ein Button aussieht, aber tatsaechlich ein React Router Link mit eigenem to Prop und eigenem onClick sein soll, ohne dass ein zusaetzliches, ueberfluessiges <button> um den Link herum gerendert wird.

Ohne Slot basierte Komponenten APIs muesste man entweder zwei verschachtelte Elemente akzeptieren, was ungueltiges HTML erzeugen kann, etwa einen Link innerhalb eines Buttons, oder man muesste fuer jede Kombination eine eigene Variante schreiben. Das Slot Pattern loest das, indem es Props und Verhalten der Elternkomponente direkt auf das einzelne uebergebene Kindelement uebertraegt, statt ein eigenes Wrapper Element zu rendern. Dieses Muster ist durch Radix UI unter dem Namen asChild bekannt geworden, funktioniert aber unabhaengig von jeder konkreten Bibliothek.

2. Das asChild Prop Grundprinzip

Das Grundprinzip einer Slot basierten Komponenten API ist, dass eine Komponente ein Boolean Prop namens asChild akzeptiert. Ist asChild gesetzt, rendert die Komponente kein eigenes DOM Element mehr, sondern nimmt children als einziges erlaubtes React Element entgegen und klont es mit cloneElement, wobei alle Props der Elternkomponente auf das geklonte Element uebertragen werden. Ist asChild nicht gesetzt, rendert die Komponente wie gewohnt ihr Standard Element, meist ein <button> oder <div>.

Der entscheidende Unterschied zum as Prop ist die Richtung der Kontrolle: Beim as Prop bestimmt ein String oder eine Komponentenreferenz, was gerendert wird. Bei einer Slot basierten Komponenten API bestimmt das tatsaechlich uebergebene Kindelement selbst, was gerendert wird, inklusive all seiner eigenen Props. Das macht Slot Komponenten besonders geeignet fuer Faelle, in denen das Zielelement bereits vollstaendig mit eigenen Props konfiguriert ist, etwa ein Router Link mit mehreren spezifischen Attributen.


// Slot.jsx — minimal slot implementation, framework agnostic
import { cloneElement, isValidElement } from "react";

function Slot({ children, ...slotProps }) {
  if (!isValidElement(children)) {
    // Slot requires exactly one valid React element as child
    return null;
  }
  // Merge props: the child's own props win over the slot's props
  return cloneElement(children, {
    ...slotProps,
    ...children.props,
    className: [slotProps.className, children.props.className].filter(Boolean).join(" "),
  });
}

function Button({ asChild, children, className, ...rest }) {
  const Component = asChild ? Slot : "button";
  return (
    <Component className={`btn ${className ?? ""}`} {...rest}>
      {children}
    </Component>
  );
}

// Usage — Link keeps its own "to" prop, no extra wrapper element rendered
// <Button asChild>
//   <Link to="/pricing">View pricing</Link>
// </Button>

3. Eigene Slot Komponente ohne Radix

Wer keine Abhaengigkeit zu Radix einfuehren will, kann die im vorherigen Abschnitt gezeigte Slot Komponente direkt uebernehmen und im eigenen Design System pflegen. Der Kern besteht aus drei Zeilen: Pruefen, ob children ein gueltiges React Element ist, das Element mit cloneElement klonen, und dabei Props zusammenfuehren. Die eigentliche Komplexitaet einer produktionsreifen Slot basierten Komponenten API steckt im Detail des Props Merging, nicht im Grundprinzip selbst.

Ein wichtiger Randfall betrifft mehrere Kindelemente: Wird asChild gesetzt und mehr als ein Kindelement uebergeben, etwa Text und ein Icon nebeneinander, schlaegt cloneElement fehl, weil children dann kein einzelnes Element mehr ist, sondern ein Array. Eine robuste Slot basierte Komponenten API prueft diesen Fall explizit und wirft im Entwicklungsmodus eine klare Fehlermeldung, damit das Problem sofort auffaellt, statt sich als stiller Rendering Fehler in der Produktion zu zeigen.


// Slot.jsx — with explicit validation for common misuse
import { Children, cloneElement, isValidElement } from "react";

function Slot({ children, ...slotProps }) {
  const childArray = Children.toArray(children);

  if (childArray.length !== 1 || !isValidElement(childArray[0])) {
    if (process.env.NODE_ENV !== "production") {
      console.error(
        "Slot expects exactly one valid React element as its child. " +
          "Received: " + childArray.length + " children."
      );
    }
    return null;
  }

  const child = childArray[0];
  return cloneElement(child, {
    ...slotProps,
    ...child.props,
  });
}

4. Named Slots: mehrere Einsatzstellen in einer Komponente

Ein einzelner asChild Slot reicht fuer eine Komponente mit genau einem austauschbaren Element. Komplexere Komponenten brauchen oft mehrere solcher Einsatzstellen gleichzeitig, etwa eine Card Komponente mit einem austauschbaren Header Element und einem austauschbaren Footer Element. Hier kommt das Muster Named Slots ins Spiel: Statt eines einzigen asChild Props definiert die Komponente mehrere benannte Props wie headerAs und footerAs, oder sie erkennt spezielle Kindkomponenten anhand eines displayName, um sie an die richtige Position im internen Markup zu verteilen.

Die zweite, in der Praxis haeufigere Variante fuer Slot basierte Komponenten APIs mit mehreren Einsatzstellen ist die Verteilung von children nach Typ. Eine Komponente durchsucht ihre Kindelemente, filtert etwa alle Elemente vom Typ Card.Header heraus und rendert sie an fester Position, waehrend der Rest normal weitergereicht wird. Diese Technik kombiniert das Slot Prinzip mit dem Compound Component Muster und eignet sich gut fuer Layout Komponenten mit mehreren klar benannten Bereichen.


// Card.jsx — named slots via child type detection
import { Children, isValidElement } from "react";

function Card({ children }) {
  const childArray = Children.toArray(children);
  const header = childArray.find((c) => isValidElement(c) && c.type === CardHeader);
  const footer = childArray.find((c) => isValidElement(c) && c.type === CardFooter);
  const body = childArray.filter(
    (c) => !(isValidElement(c) && (c.type === CardHeader || c.type === CardFooter))
  );

  return (
    <div className="card">
      {header && <div className="card-header">{header}</div>}
      <div className="card-body">{body}</div>
      {footer && <div className="card-footer">{footer}</div>}
    </div>
  );
}

function CardHeader({ children }) {
  return children;
}
function CardFooter({ children }) {
  return children;
}

Card.Header = CardHeader;
Card.Footer = CardFooter;
export { Card };

5. Props und Ref Merging bei Slots

Das schwierigste Detail jeder Slot basierten Komponenten API ist korrektes Props Merging. Event Handler wie onClick duerfen nicht einfach ueberschrieben werden, weil sonst entweder die Logik der Elternkomponente oder die Logik des Kindelements verloren geht. Der korrekte Ansatz komponiert beide Handler zu einer neuen Funktion, die zuerst den einen und dann den anderen aufruft. Gleiches gilt fuer className, das zusammengefuegt statt ersetzt werden muss, und fuer style, das als Objekt zusammengefuehrt werden sollte.

Ref Merging ist der zweite kritische Punkt. Wenn sowohl die Elternkomponente als auch der Aufrufer einen Ref auf dasselbe Element setzen wollen, braucht die Slot basierte Komponenten API eine Funktion, die mehrere Refs zusammenfuehrt und bei jeder Ref Aenderung alle betroffenen Refs aktualisiert, egal ob es sich um Funktions Refs oder Objekt Refs handelt. Ohne diese zusammengefuehrten Refs wuerde entweder der interne oder der externe Ref verloren gehen, was insbesondere bei Bibliotheken, die intern auf DOM Zugriff angewiesen sind, zu schwer auffindbaren Bugs fuehrt.

6. Slot Pattern versus Children als Function

Eine Alternative zu Slot basierten APIs ist die Children als Function Technik, bei der die Komponente eine Funktion als children erwartet und dieser Funktion Zustand und Handler als Argumente uebergibt. Der Aufrufer entscheidet dann selbst, wie das Ergebnis gerendert wird. Der Unterschied zu einer Slot basierten Komponenten API liegt darin, dass Children als Function explizit Zustand exponiert, waehrend das Slot Pattern implizit Props auf ein bestehendes Element ueberof.

In der Praxis eignet sich Children als Function besser, wenn die Komponente Zustand hat, den der Aufrufer fuer die Darstellung braucht, etwa den aktuellen Ladezustand eines Formulars. Eine Slot basierte Komponenten API eignet sich besser, wenn kein zusaetzlicher Zustand noetig ist, sondern lediglich ein zusaetzliches Wrapper Element vermieden werden soll, etwa bei einem Button, der eigentlich ein Link ist. Beide Muster lassen sich in derselben Bibliothek nebeneinander verwenden, je nach Anforderung der jeweiligen Komponente.

7. Barrierefreiheit bei Slot Komponenten

Bei einer Slot basierten Komponenten API ist Barrierefreiheit tendenziell einfacher sicherzustellen als beim as Prop, weil das tatsaechlich gerenderte Element vom Aufrufer komplett vorgegeben wird und damit bereits die richtige Semantik mitbringt. Ein Link bleibt ein Link mit allen nativen Tastatur und Screenreader Eigenschaften, unabhaengig davon, dass eine Button Komponente ihm visuelle Klassen und einen Klick Handler hinzufuegt. Es entsteht kein Zwischenelement, das zusaetzliche ARIA Semantik bräuchte.

Ein Risiko bleibt trotzdem: Wenn die Slot Komponente Props wie role oder aria-* Attribute unreflektiert ueberschreibt, statt sie mit denen des Kindelements zusammenzufuehren, kann die urspruengliche Semantik verloren gehen. Eine sorgfaeltige Slot basierte Komponenten API behandelt ARIA Attribute wie className: zusammenfuehren statt ersetzen, und im Zweifel die Werte des Kindelements bevorzugen, weil dieses naeher am tatsaechlichen Anwendungsfall ist.

8. Typische Fehler bei Slot APIs

Der haeufigste Fehler bei Slot basierten Komponenten APIs ist, mehr als ein Kindelement zu uebergeben, wenn asChild aktiv ist. Da cloneElement nur mit einem einzelnen Element funktioniert, fuehrt ein Array von Kindelementen entweder zu einem Laufzeitfehler oder zu stillem Fehlverhalten, je nach Implementierung. Ein zweiter haeufiger Fehler ist das Ueberschreiben statt Zusammenfuehren von Event Handlern, wodurch entweder die interne Logik der Komponente oder die Logik des Aufrufers verloren geht, ohne dass eine Fehlermeldung darauf hinweist.

Ein dritter Fehler betrifft TypeScript Typisierung: Weil cloneElement zur Laufzeit beliebige Props auf ein beliebiges Element anwenden kann, verliert man ohne zusaetzliche Generics schnell Typsicherheit. Eine robuste Slot basierte Komponenten API in TypeScript beschraenkt den erlaubten Kindtyp explizit auf Elemente mit kompatiblen Props, etwa ueber ein generisches ReactElement<ComponentPropsWithoutRef<"button">>, statt children als beliebiges ReactNode zu typisieren.

9. Slot Pattern im Vergleich zu Alternativen

Die folgende Tabelle vergleicht das Slot Pattern mit den beiden anderen wichtigen Ansaetzen fuer flexible Komponenten APIs.

Ansatz Zusaetzliches DOM Element Zustand exponierbar Typischer Einsatz
Slot / asChild Nein Nein, nur Props Merging Button als Link, Trigger als beliebiges Element
as Prop Ja, das gewaehlte Element selbst Nein Ueberschrift oder Text mit variablem Tag
Children als Function Optional, je nach Rueckgabe Ja, explizit als Argumente Formular Zustand, Ladezustand exponieren

In der Praxis ergaenzen sich diese drei Muster: Das Slot Pattern eignet sich, wenn ein vorhandenes Element unveraendert seine eigenen Props behalten soll. Der as Prop eignet sich, wenn nur der Elementname variieren soll. Children als Function eignet sich, wenn Zustand aus der Komponente an den Aufrufer weitergegeben werden muss. Viele produktionsreife Komponenten Bibliotheken kombinieren alle drei je nach Baustein.

Mironsoft

React Component APIs und Design Systeme

Flexible Slot basierte Komponenten fuer euer Design System?

Wir implementieren Slot Pattern, Props Merging und Ref Merging robust und typsicher, damit eure Komponenten sich mit beliebigen Kindelementen kombinieren lassen, ohne Barrierefreiheit zu verlieren.

API Design

Slot, as Prop und Children als Function passend kombinieren

Props und Ref Merging

Robuste Merge Logik ohne verlorene Handler oder Refs

TypeScript Absicherung

Generische Typen fuer Slot Kindelemente ohne any

10. Zusammenfassung

Slot basierte Komponenten APIs loesen ein spezifisches Problem im Component Design: ein Kindelement soll die Props einer Elternkomponente uebernehmen, ohne dass ein zusaetzliches Wrapper Element im DOM entsteht. Das asChild Muster von Radix hat diesen Ansatz bekannt gemacht, die zugrunde liegende Slot Komponente laesst sich aber mit cloneElement und sauberem Props Merging in wenigen Zeilen selbst implementieren, ganz ohne zusaetzliche Abhaengigkeit.

Der schwierigste Teil liegt nicht im Grundprinzip, sondern im Detail: Event Handler muessen komponiert statt ueberschrieben werden, Refs muessen zusammengefuehrt werden, und in TypeScript sollte der erlaubte Kindtyp explizit typisiert sein. Wer diese Details beherrscht, hat mit Slot basierten Komponenten APIs ein maechtiges Werkzeug, um Komponenten flexibel mit beliebigen Kindelementen zu kombinieren, ohne Barrierefreiheit oder Typsicherheit zu opfern.

Slot basierte Komponenten APIs — Das Wichtigste auf einen Blick

Grundprinzip

asChild ersetzt das Root Element durch das uebergebene Kindelement, kein Wrapper wird gerendert.

Implementierung

cloneElement plus sauberes Props Merging reicht fuer eine eigene Slot Komponente ohne Radix.

Merging

Event Handler komponieren statt ueberschreiben, Refs mit einer Merge Funktion zusammenfuehren.

Barrierefreiheit

Kein Zwischenelement noetig, Semantik des Kindelements bleibt erhalten wenn ARIA Props zusammengefuehrt werden.

11. FAQ: Slot basierte Komponenten APIs

1Was ist eine Slot basierte Komponenten API?
Das Root Element wird durch ein uebergebenes Kindelement ersetzt, das die Props der Elternkomponente uebernimmt.
2Was macht asChild?
Klont das einzelne Kindelement mit cloneElement und ueberträgt die eigenen Props darauf, ohne Wrapper.
3Brauche ich Radix dafuer?
Nein, eine minimale Slot Komponente laesst sich selbst mit cloneElement implementieren.
4Was bei mehreren Kindelementen?
cloneElement funktioniert nur mit einem Element, mehrere fuehren zu Fehler oder stillem Fehlverhalten.
5Wie Event Handler zusammenfuehren?
Neue Funktion schreiben, die beide Handler nacheinander aufruft, statt einen zu ueberschreiben.
6Wie funktioniert Ref Merging?
Eine Merge Funktion aktualisiert alle betroffenen Refs bei jeder Aenderung, egal welcher Ref Typ.
7Unterschied zum as Prop?
as waehlt ein Element per Name, Slot uebertraegt Props auf ein bereits konfiguriertes Kindelement.
8Wann Children als Function statt Slot?
Wenn Zustand fuer die Darstellung noetig ist, sonst reicht Slot fuer reines Wrapper Vermeiden.
9Wie sauber in TypeScript typisieren?
Kindtyp auf ReactElement mit kompatiblen Props einschraenken, nicht als beliebigen ReactNode typisieren.
10Wirkt sich das auf Barrierefreiheit aus?
Positiv, da kein Zwischenelement entsteht, sofern ARIA Attribute zusammengefuehrt statt ueberschrieben werden.