Combobox
Un select filtrable avec chips, groupes et résultats asynchrones, dans un popup qui se redimensionne pendant la saisie.
pnpm dlx shadcn@latest add https://hextaui.com/r/combobox.jsonAjoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.
Ajoutez les tokens de thème à votre CSS global, si ce n’est pas déjà fait.
Installez les dépendances.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopiez et collez le code suivant dans votre projet.
components/ui/combobox.tsx Mettez à jour les chemins d’import selon la configuration de votre projet.
Passez les options à items et rendez chacune avec une fonction dans <ComboboxList />. Le combobox les filtre pendant la saisie et ne rend que les correspondances. Les objets fonctionnent aussi : leur label s’affiche dans le champ et leur value est soumise.
Saisissez dans le champ pour filtrer la liste.
Un bouton affiche la valeur et le champ de recherche passe dans la popup.
Avec multiple, chaque élément sélectionné devient une chip avant le champ.
Bouton d’effacement
showClear ajoute un bouton d’effacement qui prend la place du chevron tant qu’il y a une valeur : le champ ne grandit donc jamais.
Avec des icônes
Les icônes dans un élément sont dimensionnées et atténuées pour vous. autoHighlight met en évidence la première correspondance pendant la saisie, de sorte que Enter la sélectionne.
Groupes et séparateurs
Passez des groupes de la forme { value, items } et rendez chacun avec <ComboboxGroup />, <ComboboxLabel /> et <ComboboxCollection />. Les groupes vides sont masqués pendant le filtrage.
Multiple
Avec multiple, les sélections deviennent des chips dans <ComboboxChips />. La popup reste ouverte pendant le choix, Backspace dans le champ vide supprime la dernière chip, et les flèches permettent de passer d’une chip à l’autre.
Recherche dans la popup
Utilisez <ComboboxTrigger /> pour un champ de type select. Placez le champ dans <ComboboxContent /> et il devient une zone de recherche avec une icône, et la popup s’élargit à au moins 15 rem.
Déclencheur rendu comme un Button
Passez render au déclencheur pour utiliser n’importe quel bouton. La popup s’y ancre et garde au moins sa largeur.
Contrôlé
Contrôlez la sélection avec value et onValueChange, et la popup avec open et onOpenChange. L’effacement définit la valeur à null.
Éléments désactivés, invalides et désactivés individuellement
disabled sur la racine atténue le champ et ses boutons. aria-invalid sur le champ dessine l’anneau d’erreur. Les éléments désactivés sont ignorés par les flèches.
Contenu long et grandes listes
Les libellés longs et sans coupure passent à la ligne au lieu d’élargir la popup. limit plafonne le nombre de correspondances rendues, ce qui garde rapide une liste de 500 éléments.
Recherche asynchrone
Désactivez le filtrage intégré avec filter={null}, récupérez les données dans onInputValueChange et affichez la progression dans <ComboboxStatus />, qui l’annonce aux lecteurs d’écran. La hauteur de la popup s’anime à mesure que les résultats changent.
Dans une sheet
La popup se superpose à la sheet, et Escape ferme la popup avant la sheet.
De droite à gauche
La popup reprend la direction du champ : le bouton d’effacement, les chips et les éléments s’inversent donc sans props supplémentaires.
| Touche | Action |
|---|---|
| ↓↑ | Ouvre la popup et déplace la mise en évidence parmi les correspondances. Les éléments désactivés sont ignorés. |
| Enter | Sélectionne l’élément mis en évidence. Sans mise en évidence, ferme la popup et laisse le formulaire se soumettre. |
| Escape | Ferme la popup. Si elle est déjà fermée, efface la valeur et le champ. |
| HomeEnd | Déplace le curseur de texte au début ou à la fin du champ. |
| Backspace | Dans un champ de chips vide, supprime la dernière chip. Sur une chip ayant le focus, la supprime. |
| ←→ | Avec des chips, déplace le focus entre les chips et revient au champ. Inversé dans les mises en page de droite à gauche. |
| Tab | Ferme la popup et déplace le focus plus loin. |
- Donnez au champ un
<label>visible viaidethtmlFor, ou unaria-label. Un<ComboboxTrigger />sans texte visible a aussi besoin d’unaria-label. - Le bouton du chevron est étiqueté « Show options », le bouton d’effacement « Clear selection » et le bouton de suppression de chaque chip « Remove ».
- La mise en évidence se déplace avec
aria-activedescendant, si bien que le focus reste dans le champ pendant la navigation. - Les champs utilisent une police de 16 px sur écran tactile pour qu’iOS ne zoome pas, et les éléments passent à une cible tactile de 44 px.
Construit sur le combobox de Base UI. Chaque partie accepte les props de la primitive qu’elle enveloppe ; les tableaux listent celles que vous utiliserez le plus.
| Prop | Type | Par défaut |
|---|---|---|
itemsLes options. Filtrées pendant la saisie et transmises à la fonction de rendu de la liste. | Item[] | Group[] | – |
multipleSélectionne plusieurs valeurs, affichées sous forme de chips. | 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 | – |
filterCorrespondance personnalisée. null désactive le filtrage pour une recherche côté serveur. | ((item, query, itemToString) => boolean) | null | – |
limitNombre maximal de correspondances à rendre. -1 signifie toutes. | number | -1 |
autoHighlightMet en évidence la première correspondance pendant la saisie. | boolean | false |
highlightItemOnHover | boolean | true |
openOnInputClick | boolean | true |
loopFocusReboucle la mise en évidence du dernier élément au premier. | boolean | true |
itemToStringLabelTexte affiché dans le champ pour un élément objet. | (item) => string | – |
itemToStringValueValeur soumise avec le formulaire pour un élément objet. | (item) => string | – |
isItemEqualToValue | (item, value) => boolean | – |
name | string | – |
required | boolean | false |
disabled | boolean | false |
readOnly | boolean | false |
modalVerrouille le défilement de la page et les clics extérieurs pendant l’ouverture. | boolean | false |
virtualizedÀ définir lors du rendu des éléments avec un virtualiseur. | boolean | false |
localeLocale utilisée pour la correspondance. | Intl.LocalesArgument | – |
En dehors de la popup, il rend le champ complet. Dans <ComboboxContent />, il devient une zone de recherche compacte.
| Prop | Type | Par défaut |
|---|---|---|
showTriggerAffiche le bouton du chevron. Toujours désactivé dans la popup sauf si défini. | boolean | true outside the popup |
showClearAffiche un bouton d’effacement à la place du chevron tant qu’il y a une valeur. | boolean | false |
classNameAppliqué au groupe de champ autour du champ. | string | – |
disabled | boolean | false |
placeholder | string | – |
| Attribut | Description |
|---|---|
data-slot="combobox-input-group" | Le champ autour de l’entrée. |
data-slot="combobox-input" | Le champ de texte. |
data-slot="combobox-input-actions" | Contient les boutons du chevron et d’effacement dans une seule cellule superposée. |
data-popup-open | Présent sur le champ tant que la popup est ouverte. |
data-popup-side | Le côté sur lequel la popup s’est ouverte. |
data-list-empty | Présent lorsque rien ne correspond. |
data-disabled | Présent lorsque l’élément est désactivé. |
data-invalid | Présent lorsqu’elle est invalide dans un Field de Base UI. |
| Prop | Type | Par défaut |
|---|---|---|
childrenEn général un <ComboboxValue />. Le chevron est ajouté après. | ReactNode | – |
renderLorsqu’il est défini, les styles de champ intégrés sont ignorés. | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Description |
|---|---|
data-slot="combobox-trigger" | Le bouton déclencheur. |
data-slot="combobox-trigger-value" | Enveloppe la valeur tronquée. |
data-slot="combobox-trigger-icon" | Le chevron. Se retourne à l’ouverture. |
data-popup-open | Présent tant que la popup est ouverte. |
data-placeholder | Présent tant qu’aucune valeur n’est sélectionnée. |
| Prop | Type | Par défaut |
|---|---|---|
childrenRendez vous-même la valeur sélectionnée, par exemple sous forme de chips. | ReactNode | (value) => ReactNode | – |
placeholderAffiché tant que rien n’est sélectionné. | ReactNode | – |
| Prop | Type | Par défaut |
|---|---|---|
side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "bottom" |
align | "start" | "center" | "end" | "start" |
sideOffset | number | 6 |
alignOffset | number | 0 |
anchorSe positionne par rapport à un autre élément. Par défaut, le champ. Voir useComboboxAnchor. | Element | RefObject<Element | null> | VirtualElement | (() => Element | VirtualElement | null) | null | – |
dirPar défaut, la direction du champ. | "ltr" | "rtl" | – |
| Attribut | Description |
|---|---|
data-slot="combobox-positioner" | Positionne la popup. |
data-slot="combobox-content" | La surface de la popup. |
data-slot="combobox-content-sizer" | Mesuré pour animer la hauteur de la popup lorsque les correspondances changent. |
data-open | Présent tant que l’élément est ouvert. |
data-side | Le côté sur lequel elle s’est ouverte. |
data-align | Son alignement. |
data-empty | Présent lorsque rien ne correspond. |
data-starting-style | Présent pendant l’animation d’entrée. |
data-ending-style | Présent pendant l’animation de sortie. |
--combobox-item-radius | Rayon de l’élément, déduit du rayon de la popup moins son remplissage. |
| Prop | Type | Par défaut |
|---|---|---|
childrenAppelé pour chaque correspondance de items. | ReactNode | (item, index) => ReactNode | – |
| Attribut | Description |
|---|---|
data-slot="combobox-list" | La liste défilante. |
| Prop | Type | Par défaut |
|---|---|---|
valueL’élément que cette ligne représente. | Item | – |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Description |
|---|---|
data-slot="combobox-item" | Une option. |
data-slot="combobox-item-indicator" | La coche, qui apparaît en zoom à la sélection. |
data-highlighted | Présent pendant la mise en surbrillance. |
data-selected | Présent lorsqu’il est sélectionné. |
data-disabled | Présent lorsque l’élément est désactivé. |
| Prop | Type | Par défaut |
|---|---|---|
itemsSur ComboboxGroup : les éléments propres au groupe. | Item[] | – |
childrenSur ComboboxCollection : rend chaque correspondance. | (item, index) => ReactNode | – |
| Attribut | Description |
|---|---|
data-slot="combobox-group" | Un groupe d’éléments. |
data-slot="combobox-label" | Le titre du groupe. |
<ComboboxEmpty /> n’affiche ses enfants que lorsque rien ne correspond. <ComboboxStatus /> est une région live pour les messages de chargement et de résultat. Les deux se réduisent à rien lorsqu’ils sont vides.
| Attribut | Description |
|---|---|
data-slot="combobox-empty" | Le message d’absence de résultats. |
data-slot="combobox-status" | Le message d’état live. |
data-slot="combobox-separator" | Un séparateur entre les groupes. |
| Prop | Type | Par défaut |
|---|---|---|
children | ReactNode | <IconX /> |
| Attribut | Description |
|---|---|
data-slot="combobox-clear" | Étiqueté « Clear selection ». |
data-visible | Présent tant qu’il y a quelque chose à effacer. |
| Prop | Type | Par défaut |
|---|---|---|
classNameAppliqué au champ qui enveloppe les chips. | string | – |
| Attribut | Description |
|---|---|
data-slot="combobox-chips" | Le champ qui contient les chips et la saisie. |
| Prop | Type | Par défaut |
|---|---|---|
showRemoveAffiche le bouton de suppression. | boolean | true |
| Attribut | Description |
|---|---|
data-slot="combobox-chip" | Une valeur sélectionnée. |
data-slot="combobox-chip-label" | Son libellé tronqué. |
data-slot="combobox-chip-remove" | Étiqueté « Remove ». |
Le champ de texte placé après les chips. Accepte les mêmes props que l’input de Base UI.
| Attribut | Description |
|---|---|
data-slot="combobox-chips-input" | Le champ des chips. |
useComboboxAnchor()retourne une ref à passer à un élément et àanchorsur le contenu.useComboboxFilter()retourne des comparateurscontains,startsWithetendsWithsensibles à la locale pourfilter.useComboboxFilteredItems()lit les correspondances actuelles, pour les compteurs ou les listes virtualisées.createComboboxItems(data, { getValue })construit une collection d’éléments dont la valeur de sélection est un id primitif, comme une clé de base de données, plutôt que l’objet entier.comboboxFieldVariantsexpose les styles de champ pour construire des champs personnalisés.
- CalendarUne grille de dates pour la sélection simple, de plage et multiple, avec des mois qui défilent, des aperçus de plage et des jours à taille tactile.
- CheckboxUne case à cocher dont la coche se dessine, avec des parents indéterminés, des groupes et des libellés qui partagent son survol.
- Date pickerUn bouton qui ouvre un calendrier dans un popover, ou une bottom sheet sur mobile, pour des dates simples et des plages.
- FieldDes libellés, descriptions et erreurs reliés à leur contrôle, avec états de validation et mises en page pour les formulaires.
- InputUn champ de saisie texte en trois tailles, avec états invalide et lecture seule, style de validation natif et police tactile de 16px pour que les téléphones ne zooment jamais.
- Input groupUn champ de saisie avec icônes, texte, boutons ou indice clavier attachés, partageant une même bordure et un même anneau de focus.