Combobox
Ein filterbares Select mit Chips, Gruppen und asynchronen Ergebnissen, in einem Popup, das sich beim Tippen anpasst.
pnpm dlx shadcn@latest add https://hextaui.com/r/combobox.jsonFügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.
Füge die Theme-Tokens zu deinem globalen CSS hinzu, falls du das noch nicht getan hast.
Installiere die Abhängigkeiten.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnKopiere den folgenden Code und füge ihn in dein Projekt ein.
components/ui/combobox.tsx Passe die Importpfade an dein Projekt-Setup an.
Übergib die Optionen an items und rendere jede mit einer Funktion in <ComboboxList />. Die Combobox filtert sie beim Tippen und rendert nur die Treffer. Objekte funktionieren auch: Ihr label wird im Input angezeigt und ihr value gesendet.
Tippe in das Feld, um die Liste zu filtern.
Ein Button zeigt den Wert, und das Suchfeld wandert in das Popup.
Mit multiple wird jeder ausgewählte Eintrag zu einem Chip vor dem Input.
Löschen-Button
showClear fügt einen Löschen-Button hinzu, der den Platz des Chevrons einnimmt, solange ein Wert vorhanden ist, sodass das Feld nie wächst.
Mit Icons
Icons in einem Eintrag werden automatisch dimensioniert und gedämpft. autoHighlight hebt den ersten Treffer beim Tippen hervor, sodass Enter ihn auswählt.
Gruppen und Trennlinien
Übergib Gruppen der Form { value, items } und rendere jede mit <ComboboxGroup />, <ComboboxLabel /> und <ComboboxCollection />. Leere Gruppen werden beim Filtern ausgeblendet.
Mehrfach
Mit multiple werden Auswahlen zu Chips in <ComboboxChips />. Das Popup bleibt beim Auswählen offen, Backspace im leeren Input entfernt den letzten Chip, und die Pfeiltasten wechseln zwischen Chips.
Suche im Popup
Verwende <ComboboxTrigger /> für ein select-ähnliches Feld. Setze das Input in <ComboboxContent />, und es wird zu einem Suchfeld mit Icon, und das Popup wird mindestens 15 rem breit.
Trigger als Button gerendert
Übergib render an den Trigger, um einen beliebigen Button zu verwenden. Das Popup verankert sich daran und behält mindestens dessen Breite.
Kontrolliert
Steuere die Auswahl mit value und onValueChange und das Popup mit open und onOpenChange. Das Leeren setzt den Wert auf null.
Deaktivierte, ungültige und deaktivierte Einträge
disabled an der Root dimmt das Feld und seine Buttons. aria-invalid am Input zeichnet den Fehlerring. Deaktivierte Einträge werden von den Pfeiltasten übersprungen.
Langer Inhalt und große Listen
Lange Labels und Labels ohne Umbruchstelle werden umgebrochen, statt das Popup zu verbreitern. limit begrenzt, wie viele Treffer gerendert werden, was eine Liste mit 500 Einträgen schnell hält.
Asynchrone Suche
Schalte das integrierte Filtern mit filter={null} ab, lade Daten bei onInputValueChange und zeige den Fortschritt in <ComboboxStatus /> an, das ihn Screenreadern ansagt. Die Popup-Höhe animiert, wenn sich die Ergebnisse ändern.
In einem Sheet
Das Popup liegt über dem Sheet, und Escape schließt zuerst das Popup, dann das Sheet.
Rechts nach links
Das Popup übernimmt die Richtung des Felds, sodass Löschen-Button, Chips und Einträge ohne zusätzliche Props gespiegelt werden.
| Taste | Aktion |
|---|---|
| ↓↑ | Öffnet das Popup und bewegt die Hervorhebung durch die Treffer. Deaktivierte Einträge werden übersprungen. |
| Enter | Wählt den hervorgehobenen Eintrag aus. Ist nichts hervorgehoben, schließt es das Popup und lässt das Formular absenden. |
| Escape | Schließt das Popup. Ist es bereits geschlossen, werden Wert und Input geleert. |
| HomeEnd | Setzt den Textcursor an den Anfang oder das Ende des Inputs. |
| Backspace | In einem leeren Chips-Input wird der letzte Chip entfernt. Auf einem fokussierten Chip wird dieser entfernt. |
| ←→ | Mit Chips wechselt der Fokus zwischen Chips und zurück zum Input. In Rechts-nach-links-Layouts gespiegelt. |
| Tab | Schließt das Popup und setzt den Fokus weiter. |
- Gib dem Input über
idundhtmlForein sichtbares<label>oder einaria-label. Ein<ComboboxTrigger />ohne sichtbaren Text braucht ebenfalls einaria-label. - Der Chevron-Button ist mit „Show options“ beschriftet, der Löschen-Button mit „Clear selection“ und der Entfernen-Button jedes Chips mit „Remove“.
- Die Hervorhebung wandert mit
aria-activedescendant, sodass der Fokus beim Durchsuchen im Input bleibt. - Inputs verwenden auf Touchscreens eine 16-px-Schrift, damit iOS nicht zoomt, und Einträge wachsen auf ein 44-px-Tippziel.
Basiert auf der Base UI Combobox. Jeder Teil akzeptiert die Props der Primitive, die er umschließt; die Tabellen listen die am häufigsten genutzten auf.
| Prop | Typ | Standard |
|---|---|---|
itemsDie Optionen. Werden beim Tippen gefiltert und an die Render-Funktion der Liste übergeben. | Item[] | Group[] | – |
multipleMehrere Werte auswählen, als Chips angezeigt. | boolean | false |
value | Value | Value[] | null | – |
defaultValue | Value | Value[] | null | – |
onValueChange | (value, details) => void | – |
open | boolean | – |
defaultOpen | boolean | false |
onOpenChange | (open: boolean, details) => void | – |
inputValue | string | – |
defaultInputValue | string | – |
onInputValueChange | (inputValue: string, details) => void | – |
filterEigenes Matching. null schaltet das Filtern für serverseitige Suche ab. | ((item, query, itemToString) => boolean) | null | – |
limitMaximale Anzahl gerenderter Treffer. -1 bedeutet alle. | number | -1 |
autoHighlightHebt den ersten Treffer beim Tippen hervor. | boolean | false |
highlightItemOnHover | boolean | true |
openOnInputClick | boolean | true |
loopFocusDie Hervorhebung vom letzten zum ersten Eintrag umbrechen. | boolean | true |
itemToStringLabelText, der im Input für einen Objekt-Eintrag angezeigt wird. | (item) => string | – |
itemToStringValueWert, der bei einem Objekt-Eintrag mit dem Formular gesendet wird. | (item) => string | – |
isItemEqualToValue | (item, value) => boolean | – |
name | string | – |
required | boolean | false |
disabled | boolean | false |
readOnly | boolean | false |
modalSperrt das Scrollen der Seite und Klicks nach außen, solange geöffnet. | boolean | false |
virtualizedSetzen, wenn Einträge mit einem Virtualizer gerendert werden. | boolean | false |
localeLocale, die für das Matching verwendet wird. | Intl.LocalesArgument | – |
Außerhalb des Popups rendert es das vollständige Feld. In <ComboboxContent /> wird es zu einem kompakten Suchfeld.
| Prop | Typ | Standard |
|---|---|---|
showTriggerZeigt den Chevron-Button. Im Popup immer aus, sofern nicht gesetzt. | boolean | true outside the popup |
showClearZeigt einen Löschen-Button anstelle des Chevrons, solange ein Wert vorhanden ist. | boolean | false |
classNameWird auf die Input-Gruppe um das Input angewendet. | string | – |
disabled | boolean | false |
placeholder | string | – |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-input-group" | Das Feld um das Input. |
data-slot="combobox-input" | Das Texteingabefeld. |
data-slot="combobox-input-actions" | Hält die Buttons für Chevron und Löschen in einer gestapelten Zelle. |
data-popup-open | Vorhanden am Input, solange das Popup geöffnet ist. |
data-popup-side | Die Seite, auf der sich das Popup geöffnet hat. |
data-list-empty | Vorhanden, wenn nichts passt. |
data-disabled | Vorhanden, wenn deaktiviert. |
data-invalid | Vorhanden, wenn ungültig innerhalb eines Base UI Field. |
| Prop | Typ | Standard |
|---|---|---|
childrenNormalerweise ein <ComboboxValue />. Der Chevron wird danach hinzugefügt. | ReactNode | – |
renderWenn gesetzt, werden die integrierten Feld-Stile übersprungen. | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-trigger" | Der Trigger-Button. |
data-slot="combobox-trigger-value" | Umschließt den gekürzten Wert. |
data-slot="combobox-trigger-icon" | Der Chevron. Kippt, solange geöffnet. |
data-popup-open | Vorhanden, solange das Popup geöffnet ist. |
data-placeholder | Vorhanden, solange kein Wert ausgewählt ist. |
| Prop | Typ | Standard |
|---|---|---|
childrenRendere den ausgewählten Wert selbst, zum Beispiel als Chips. | ReactNode | (value) => ReactNode | – |
placeholderWird angezeigt, solange nichts ausgewählt ist. | ReactNode | – |
| Prop | Typ | Standard |
|---|---|---|
side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "bottom" |
align | "start" | "center" | "end" | "start" |
sideOffset | number | 6 |
alignOffset | number | 0 |
anchorPositioniert sich an einem anderen Element. Standardmäßig das Feld. Siehe useComboboxAnchor. | Element | RefObject<Element | null> | VirtualElement | (() => Element | VirtualElement | null) | null | – |
dirStandardmäßig die Richtung des Felds. | "ltr" | "rtl" | – |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-positioner" | Positioniert das Popup. |
data-slot="combobox-content" | Die Popup-Fläche. |
data-slot="combobox-content-sizer" | Wird gemessen, um die Höhe des Popups zu animieren, wenn sich die Treffer ändern. |
data-open | Vorhanden, solange geöffnet. |
data-side | Die Seite, auf der es sich geöffnet hat. |
data-align | Seine Ausrichtung. |
data-empty | Vorhanden, wenn nichts passt. |
data-starting-style | Vorhanden während der Einblendanimation. |
data-ending-style | Vorhanden während der Ausblendanimation. |
--combobox-item-radius | Radius des Eintrags, abgeleitet vom Popup-Radius abzüglich seines Paddings. |
| Prop | Typ | Standard |
|---|---|---|
childrenWird für jeden Treffer in items aufgerufen. | ReactNode | (item, index) => ReactNode | – |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-list" | Die scrollende Liste. |
| Prop | Typ | Standard |
|---|---|---|
valueDer Eintrag, den diese Zeile darstellt. | Item | – |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-item" | Eine Option. |
data-slot="combobox-item-indicator" | Der Haken, der bei Auswahl hereinskaliert. |
data-highlighted | Vorhanden, solange hervorgehoben. |
data-selected | Vorhanden, wenn ausgewählt. |
data-disabled | Vorhanden, wenn deaktiviert. |
| Prop | Typ | Standard |
|---|---|---|
itemsAuf ComboboxGroup: die eigenen Einträge der Gruppe. | Item[] | – |
childrenAuf ComboboxCollection: rendert jeden Treffer. | (item, index) => ReactNode | – |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-group" | Eine Gruppe von Einträgen. |
data-slot="combobox-label" | Die Gruppenüberschrift. |
<ComboboxEmpty /> zeigt seine Kinder nur, wenn nichts passt. <ComboboxStatus /> ist eine Live-Region für Lade- und Ergebnismeldungen. Beide fallen im leeren Zustand zu nichts zusammen.
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-empty" | Die Meldung „keine Ergebnisse“. |
data-slot="combobox-status" | Die Live-Statusmeldung. |
data-slot="combobox-separator" | Eine Trennlinie zwischen Gruppen. |
| Prop | Typ | Standard |
|---|---|---|
children | ReactNode | <IconX /> |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-clear" | Beschriftet mit „Clear selection“. |
data-visible | Vorhanden, solange es etwas zu leeren gibt. |
| Prop | Typ | Standard |
|---|---|---|
classNameWird auf das Feld angewendet, das die Chips umschließt. | string | – |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-chips" | Das Feld, das Chips und Input enthält. |
| Prop | Typ | Standard |
|---|---|---|
showRemoveZeigt den Entfernen-Button. | boolean | true |
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-chip" | Ein ausgewählter Wert. |
data-slot="combobox-chip-label" | Sein gekürztes Label. |
data-slot="combobox-chip-remove" | Beschriftet mit „Remove“. |
Das Texteingabefeld nach den Chips. Akzeptiert dieselben Props wie das Base UI Input.
| Attribut | Beschreibung |
|---|---|
data-slot="combobox-chips-input" | Das Chips-Input. |
useComboboxAnchor()gibt eine Ref zurück, die du an ein Element und ananchordes Contents übergibst.useComboboxFilter()gibt locale-abhängige Matchercontains,startsWithundendsWithfürfilterzurück.useComboboxFilteredItems()liest die aktuellen Treffer, für Zähler oder virtualisierte Listen.createComboboxItems(data, { getValue })baut eine Eintragssammlung, deren Auswahlwert eine primitive ID ist, etwa ein Datenbankschlüssel, statt des ganzen Objekts.comboboxFieldVariantsstellt die Feld-Stile bereit, um eigene Felder zu bauen.
- CalendarEin Datumsraster für Einzel-, Bereichs- und Mehrfachauswahl, mit gleitenden Monaten, Bereichsvorschau und Tagen in Touch-Größe.
- CheckboxEine Checkbox, deren Häkchen sich einzeichnet, mit unbestimmten übergeordneten Elementen, Gruppen und Labels, die ihren Hover teilen.
- Date pickerEin Button, der einen Kalender in einem Popover öffnet, auf Smartphones als Bottom Sheet, für einzelne Daten und Zeiträume.
- FieldLabels, Beschreibungen und Fehler, mit ihrem Steuerelement verbunden, mit Validierungszuständen und Layouts für Formulare.
- InputEin Texteingabefeld mit drei Größen, ungültigen und schreibgeschützten Zuständen, nativem Validierungsstyling und einer 16-px-Schrift für Touch, damit Smartphones nie hineinzoomen.
- Input groupEin Input mit angehängten Icons, Text, Buttons oder Tastaturhinweis, die sich einen Rahmen und einen Fokusring teilen.