Input group
Un 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.
pnpm dlx shadcn@latest add https://hextaui.com/r/input-group.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/input-group.tsx components/ui/input.tsx components/ui/button.tsx components/ui/number-flow.tsx lib/motion.ts Mettez à jour les chemins d’import selon la configuration de votre projet.
Placez <InputGroupInput /> ou <InputGroupTextarea /> en premier et les addons après. Les addons se placent eux-mêmes avec align, si bien que le champ vient en premier dans l’ordre de tabulation et pour les lecteurs d’écran.
Le groupe suit son champ : les parties intelligentes n’ont donc besoin d’aucun câblage. <InputGroupClear />, <InputGroupPasswordToggle /> et <InputGroupCount /> lisent la valeur, type et maxLength du champ, qu’il soit contrôlé ou non.
Icône
Les icônes se placent à l’intérieur de la bordure de chaque côté. Cliquer sur une icône donne le focus au champ : tout le groupe se comporte donc comme un seul champ.
Effacer
<InputGroupClear /> apparaît en fondu dès qu’il y a une valeur. Il efface via l’historique d’édition du navigateur, si bien que Cmd+Z ramène le texte, et il déclenche votre onChange. Escape efface aussi. Un second Escape est laissé à la boîte de dialogue ou au popover autour.
Compteur de caractères
<InputGroupCount /> compte par rapport au maxLength du champ. Seuls les chiffres qui changent défilent. Le compteur fonce près de la limite et devient rouge lorsqu’elle est atteinte, et une touche tapée au-delà le fait légèrement vibrer. Les lecteurs d’écran entendent un message lorsque le champ approche de la limite et lorsqu’il l’atteint, jamais à chaque touche.
Texte
Utilisez <InputGroupText /> pour les unités, les devises et les parties d’URL. Le remplissage du champ diminue à côté d’un addon pour que le texte se lise comme une seule valeur.
Button
<InputGroupButton /> est un bouton ghost dimensionné pour tenir dans le champ. Ses coins sont concentriques à ceux du groupe, et il a son propre anneau de focus.
Indication clavier
Un <kbd> simple dans un addon est stylisé comme une touche de clavier. C’est uniquement un repère visuel : associez le raccourci vous-même.
Textarea
<InputGroupTextarea /> grandit avec son contenu jusqu’à 16 rem, puis défile. La hauteur s’adapte en douceur d’une ligne à l’autre au lieu de sauter. Un addon block-end devient une barre d’outils en dessous, et les boutons sur ses bords reçoivent le même retrait que le coin où ils se trouvent.
En-tête
Un addon block-start se place au-dessus du champ. Ajoutez separator pour tracer un filet fin entre les deux.
Mot de passe
<InputGroupPasswordToggle /> fait passer un champ type="password" en texte et inversement. Le curseur et la sélection restent où ils étaient, un clic de souris garde le focus dans le champ, et le mot de passe est de nouveau masqué à la soumission du formulaire. Contrôlez-le avec revealed.
Tailles
size sur le groupe définit la hauteur et la transmet au champ, en cohérence avec les tailles de <Input />. Les boutons gardent des coins concentriques à toutes les tailles.
Invalide
Définissez aria-invalid sur le champ et tout le groupe devient rouge, y compris son anneau de focus. Reliez le message avec aria-describedby.
Désactivé
Un champ désactivé atténue tout le groupe et affiche un curseur d’interdiction dessus. Désactivez aussi les boutons des addons, car ils restent sinon utilisables.
Chargement
Les addons en ligne s’adaptent en douceur à leur nouvelle largeur lorsque leur contenu change : le champ ne saute donc jamais quand un spinner devient un nombre de résultats. Le spinner ne tourne que si l’animation est autorisée, et role="status" annonce le texte.
Dropdown
Rendez un <InputGroupButton /> comme déclencheur de menu déroulant pour restreindre la portée de la saisie.
Contenu long
Les valeurs longues défilent dans le champ au lieu d’étirer le groupe. Enveloppez un long texte d’addon dans un span tronquant avec une largeur maximale.
De droite à gauche
Les addons, le remplissage et les rayons de coin utilisent des côtés logiques : inline-start se place donc à droite.
| Touche | Action |
|---|---|
| Tab | Passe du champ à chaque bouton d’addon, dans l’ordre du source. |
| ShiftTab | Revient en arrière parmi les boutons et le champ. |
| Escape | Avec un InputGroupClear, efface le champ. S’il est déjà vide, Escape passe au travers. |
- Chaque champ a besoin d’un nom. Utilisez un libellé visible, un Field ou
aria-label. Les icônes et le texte des addons ne font pas partie du nom du champ. - Donnez un
aria-labelaux boutons réduits à une icône. - Lorsque le texte d’un addon porte du sens, comme une devise ou un domaine, ajoutez-le au libellé ou référencez-le avec
aria-describedby. <InputGroupClear />est ignoré dans l’ordre de tabulation, car Escape fait la même chose. Le bouton d’affichage du mot de passe reste atteignable par tabulation et garde le même nom, avecaria-pressedpour indiquer son état.- Lorsqu’une soumission trouve le champ invalide, le groupe vibre une fois. Avec la réduction des animations, la bordure rouge est le seul indice.
- Sur écran tactile, le texte du champ fait au moins 16 px pour que les téléphones ne zooment pas lorsqu’il reçoit le focus.
<InputGroupInput /> et <InputGroupTextarea /> acceptent les props des éléments qu’ils rendent. Les autres parties acceptent les attributs de leur élément.
| Prop | Type | Par défaut |
|---|---|---|
sizeHauteur du groupe, transmise au champ. | "sm" | "default" | "lg" | "default" |
| Attribut | Description |
|---|---|
data-slot="input-group" | Ciblez le groupe en CSS. Rend role="group". |
data-size | La taille actuelle. |
data-filled | Présent tant que le champ a une valeur. |
data-shake | Présent pendant que le groupe vibre après une soumission échouée. |
data-disabled | Définissez-le vous-même pour atténuer le groupe lorsque seuls les addons sont désactivés. |
--input-group-radius | Rayon des coins du groupe. Les boutons et les touches en déduisent leur rayon. |
--input-group-height | Hauteur du groupe pour la taille actuelle. |
| Prop | Type | Par défaut |
|---|---|---|
sizeHérité du groupe. | "sm" | "default" | "lg" | – |
aria-invalid | boolean | – |
disabled | boolean | false |
readOnly | boolean | false |
| Attribut | Description |
|---|---|
data-slot="input-group-control" | Marque le champ. Le groupe en lit les états de focus, d’invalidité, de désactivation et de lecture seule. |
data-invalid | Présent lorsqu’un Field englobant marque la valeur comme invalide. |
data-disabled | Présent lorsque le champ est désactivé. |
data-focused | Présent tant que le champ a le focus. |
data-filled | Présent lorsque le champ a une valeur. |
data-dirty | Présent une fois que la valeur diffère de la valeur initiale. |
data-touched | Présent une fois que le champ a reçu puis perdu le focus. |
| Prop | Type | Par défaut |
|---|---|---|
autoResizeGrandit avec le contenu jusqu’à 16 rem, avec une transition douce entre les hauteurs. | boolean | true |
shakeFait vibrer le groupe lorsqu’une soumission le trouve invalide. | boolean | true |
rows | number | – |
aria-invalid | boolean | – |
disabled | boolean | false |
| Attribut | Description |
|---|---|
data-slot="input-group-control" | Marque le champ. Le groupe en lit les états de focus, d’invalidité, de désactivation et de lecture seule. |
data-invalid | Présent lorsqu’un Field englobant marque la valeur comme invalide. |
data-disabled | Présent lorsque le champ est désactivé. |
data-focused | Présent tant que le champ a le focus. |
data-filled | Présent lorsque le champ a une valeur. |
data-dirty | Présent une fois que la valeur diffère de la valeur initiale. |
data-touched | Présent une fois que le champ a reçu puis perdu le focus. |
| Prop | Type | Par défaut |
|---|---|---|
align | "inline-start" | "inline-end" | "block-start" | "block-end" | "inline-start" |
separatorTrace un filet fin entre un addon de bloc et le champ. | boolean | false |
| Attribut | Description |
|---|---|
data-slot="input-group-addon" | Ciblez les addons en CSS. |
data-align | L’alignement actuel. |
data-separator | Présent lorsque separator est défini. |
--input-group-addon-inset | Espace entre le bord du groupe et un bouton ou une touche à l’intérieur. |
| Prop | Type | Par défaut |
|---|---|---|
variant | "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | "ghost" |
size | "xs" | "sm" | "icon-xs" | "icon-sm" | "xs" |
type | string | "button" |
feedbackToutes les props de Button fonctionnent, y compris le flux de chargement et de succès. | boolean | false |
| Attribut | Description |
|---|---|
data-slot="input-group-button" | Ciblez les boutons d’addon en CSS. |
data-size | La taille actuelle. |
| Prop | Type | Par défaut |
|---|---|---|
onClearAppelé après l’effacement du champ. | () => void | – |
aria-label | string | "Clear" |
children | ReactNode | <IconX /> |
| Attribut | Description |
|---|---|
data-slot="input-group-clear" | Ciblez le bouton d’effacement en CSS. |
data-visible | Présent tant que le champ a une valeur et est modifiable. |
| Prop | Type | Par défaut |
|---|---|---|
revealedÉtat contrôlé. Laissez non défini pour qu’il se gère lui-même. | boolean | – |
onRevealedChange | (revealed: boolean) => void | – |
aria-label | string | "Show password" |
| Attribut | Description |
|---|---|
data-slot="input-group-password-toggle" | Ciblez le bouton d’affichage en CSS. |
data-revealed | Présent tant que le mot de passe est affiché. |
| Prop | Type | Par défaut |
|---|---|---|
thresholdCaractères restants à partir desquels le compteur se démarque. | number | 10% of maxLength, at most 20 |
announcementMessage pour les lecteurs d’écran lorsque le compteur franchit le seuil ou atteint la limite. | (remaining: number) => string | – |
| Attribut | Description |
|---|---|
data-slot="input-group-count" | Ciblez le compteur en CSS. |
data-state="near" | "limit" | Présent dans le seuil, et lorsqu’il ne reste plus de caractères. |
data-bump | Présent brièvement lorsqu’une touche est pressée à la limite. |
| Attribut | Description |
|---|---|
data-slot="input-group-text" | Ciblez le texte d’addon en CSS. |
- ButtonDes boutons dans toutes les variantes et tailles, avec un flux de chargement, de succès et d’erreur intégré qui évite le spinner pour les requêtes rapides.
- 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.
- MotionLes courbes d’easing, les durées et la vérification de réduction des animations utilisées par chaque composant, ainsi que des hooks pour les morphs de taille et les surlignages glissants.
- useAutosizeAgrandit un textarea selon ce que vous écrivez, entre sa hauteur minimale et maximale, en animant chaque changement sans jamais toucher au texte.
- useComposedRefConserve une ref vers votre propre élément tout en la transmettant à la ref passée par le parent.
- useInvalidShakeFait vibrer un contrôle de formulaire lorsqu’une tentative d’envoi le trouve invalide, et jamais pendant que quelqu’un tape encore.
Utilisé dans les blocks
Des blocks qui s’appuient sur Input group.
- BillingForfait et utilisation pour un produit d’IA, dans le style de Cursor, Claude et Vercel. Un compteur d’utilisation réparti par modèle qui projette la fin du cycle et prévient avant la fin des crédits, un graphique quotidien explorable, une limite de dépenses avec des alertes prévisualisables sur le compteur, des changements de forfait avec proratisation exacte, un formulaire de carte avec une vraie validation, et des factures téléchargeables en PDF.
- Diff ReviewRelisez les modifications d’un agent dans plusieurs fichiers avant qu’elles ne soient appliquées. Une arborescence de fichiers avec compteurs, acceptation ou rejet de chaque changement, de chaque fichier ou de tout, des commentaires sur n’importe quelle ligne ou plage renvoyés à l’agent, des vues unifiée et côte à côte, des surlignages au niveau du mot, l’annulation, des modifications en streaming et un résumé « 4 fichiers modifiés » pour le chat.
- ModelsLa page Modèles des paramètres d’un produit d’IA. Un modèle par défaut avec son contexte, sa vitesse et son coût en un coup d’œil, un effort par défaut qui sait ce que prend en charge chaque modèle, une liste de modèles consultable groupée par fournisseur avec filtres, épingles et bascules groupées, des serveurs compatibles OpenAI avec un vrai test de connexion, et une actualisation qui indique les nouveautés.
- ProfileLa section Profil des paramètres d’un produit d’IA. Recadrez une photo en cercle, choisissez un nom d’utilisateur vérifié pendant la saisie, confirmez une nouvelle adresse e-mail avec un code à 6 chiffres, ajoutez des liens qui reconnaissent le site et voyez une carte en direct de ce que les autres voient de vous.