Input group
Un input con iconos, texto, botones o una pista de teclado acoplados, que comparten un solo borde y anillo de foco.
pnpm dlx shadcn@latest add https://hextaui.com/r/input-group.jsonAñade el componente, los tokens del tema de HextaUI y los componentes de HextaUI de los que depende.
Añade los tokens del tema a tu CSS global, si aún no lo has hecho.
Instala las dependencias.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopia y pega el siguiente código en tu proyecto.
components/ui/input-group.tsx components/ui/input.tsx components/ui/button.tsx components/ui/number-flow.tsx lib/motion.ts Actualiza las rutas de importación para que coincidan con la configuración de tu proyecto.
Pon <InputGroupInput /> o <InputGroupTextarea /> primero y los addons después. Los addons se colocan solos con align, así que el campo va primero en el orden de tabulación y para los lectores de pantalla.
El grupo sigue a su campo, así que las partes inteligentes no necesitan conexión. <InputGroupClear />, <InputGroupPasswordToggle /> y <InputGroupCount /> leen el valor, type y maxLength del campo, sea controlado o no.
Icono
Los iconos van dentro del borde a ambos lados. Hacer clic en un icono da foco al input, así que todo el grupo se siente como un solo campo.
Borrar
<InputGroupClear /> aparece con un fundido cuando hay un valor. Borra a través del historial de edición del navegador, así que Cmd+Z devuelve el texto, y dispara tu onChange. Escape también borra. Un segundo Escape se deja para el diálogo o popover que lo rodea.
Contador de caracteres
<InputGroupCount /> cuenta respecto al maxLength del campo. Solo ruedan los dígitos que cambian. El recuento se oscurece cerca del límite y se vuelve rojo al alcanzarlo, y una tecla pulsada más allá del límite lo sacude ligeramente. Los lectores de pantalla oyen un mensaje cuando el campo se acerca al límite y cuando lo alcanza, nunca en cada tecla.
Texto
Usa <InputGroupText /> para unidades, monedas y partes de URL. El relleno del input se reduce junto a un addon para que el texto se lea como un solo valor.
Button
<InputGroupButton /> es un botón ghost dimensionado para caber dentro del campo. Sus esquinas son concéntricas con las del grupo y tiene su propio anillo de foco.
Pista de teclado
Un <kbd> simple dentro de un addon recibe estilo de tecla. Es solo una pista visual, así que enlaza el atajo tú mismo.
Textarea
<InputGroupTextarea /> crece con su contenido hasta 16rem y luego se desplaza. La altura se ajusta suavemente entre líneas en lugar de saltar. Un addon block-end se convierte en una barra de herramientas debajo, y los botones en sus bordes reciben el mismo margen interior que la esquina en que se sitúan.
Encabezado
Un addon block-start se sitúa sobre el campo. Añade separator para dibujar una línea fina entre ambos.
Contraseña
<InputGroupPasswordToggle /> cambia un campo type="password" a texto y viceversa. El cursor y la selección se quedan donde estaban, un clic del ratón mantiene el foco en el campo, y la contraseña vuelve a ocultarse al enviar el formulario. Contrólalo con revealed.
Tamaños
size en el grupo define la altura y se pasa al input, coincidiendo con los tamaños de <Input />. Los botones conservan esquinas concéntricas en todos los tamaños.
Inválido
Define aria-invalid en el input y todo el grupo se vuelve rojo, incluido su anillo de foco. Vincula el mensaje con aria-describedby.
Deshabilitado
Un input deshabilitado atenúa todo el grupo y muestra un cursor de no permitido sobre él. Deshabilita también los botones de los addons, ya que de otro modo siguen siendo utilizables.
Cargando
Los addons en línea se ajustan suavemente a su nuevo ancho cuando cambia su contenido, así que el campo nunca salta cuando un spinner se convierte en un recuento de resultados. El spinner solo gira cuando se permite el movimiento, y role="status" anuncia el texto.
Dropdown
Renderiza un <InputGroupButton /> como trigger de un dropdown para acotar el input.
Contenido largo
Los valores largos se desplazan dentro del input en lugar de estirar el grupo. Envuelve el texto largo de un addon en un span que se trunque con un ancho máximo.
De derecha a izquierda
Los addons, el relleno y los radios de las esquinas usan lados lógicos, así que inline-start queda a la derecha.
| Key | Acción |
|---|---|
| Tab | Pasa del campo a cada botón de los addons, en el orden del código fuente. |
| ShiftTab | Retrocede por los botones y el campo. |
| Escape | Con un InputGroupClear, borra el campo. Si ya está vacío, Escape pasa de largo. |
- Cada campo necesita un nombre. Usa una etiqueta visible, un Field o
aria-label. Los iconos y el texto de los addons no forman parte del nombre del campo. - Dale a los botones solo con icono un
aria-label. - Cuando el texto de un addon tiene significado, como una moneda o un dominio, añádelo a la etiqueta o referénciaslo con
aria-describedby. <InputGroupClear />se omite en el orden de tabulación porque Escape hace lo mismo. El interruptor de contraseña sigue siendo tabulable y conserva el mismo nombre, conaria-pressedinformando de su estado.- Cuando un envío encuentra el campo no válido, el grupo se sacude una vez. Con movimiento reducido, el borde rojo es la única señal.
- En pantallas táctiles el texto del campo es de al menos 16px para que los teléfonos no hagan zoom cuando recibe foco.
<InputGroupInput /> y <InputGroupTextarea /> aceptan las props de los elementos que renderizan. Las demás partes aceptan los atributos de su elemento.
| Prop | Tipo | Predeterminado |
|---|---|---|
sizeAltura del grupo, pasada al input. | "sm" | "default" | "lg" | "default" |
| Atributo | Descripción |
|---|---|
data-slot="input-group" | Selecciona el grupo en CSS. Renderiza role="group". |
data-size | El tamaño actual. |
data-filled | Presente mientras el campo tiene un valor. |
data-shake | Presente mientras el grupo se sacude tras un envío fallido. |
data-disabled | Defínelo tú mismo para atenuar el grupo cuando solo los addons están deshabilitados. |
--input-group-radius | Radio de las esquinas del grupo. Los botones y las teclas derivan su radio de él. |
--input-group-height | Altura del grupo para el tamaño actual. |
| Prop | Tipo | Predeterminado |
|---|---|---|
sizeSe hereda del grupo. | "sm" | "default" | "lg" | – |
aria-invalid | boolean | – |
disabled | boolean | false |
readOnly | boolean | false |
| Atributo | Descripción |
|---|---|
data-slot="input-group-control" | Marca el campo. El grupo lee de él sus estados de foco, no válido, deshabilitado y solo lectura. |
data-invalid | Presente cuando un Field circundante marca el valor como no válido. |
data-disabled | Presente cuando el campo está deshabilitado. |
data-focused | Presente mientras el campo tiene foco. |
data-filled | Presente cuando el campo tiene un valor. |
data-dirty | Presente una vez que el valor difiere del inicial. |
data-touched | Presente una vez que el campo ha recibido foco y lo ha perdido. |
| Prop | Tipo | Predeterminado |
|---|---|---|
autoResizeCrece con el contenido hasta 16rem, ajustándose suavemente entre alturas. | boolean | true |
shakeSacude el grupo cuando un envío lo encuentra no válido. | boolean | true |
rows | number | – |
aria-invalid | boolean | – |
disabled | boolean | false |
| Atributo | Descripción |
|---|---|
data-slot="input-group-control" | Marca el campo. El grupo lee de él sus estados de foco, no válido, deshabilitado y solo lectura. |
data-invalid | Presente cuando un Field circundante marca el valor como no válido. |
data-disabled | Presente cuando el campo está deshabilitado. |
data-focused | Presente mientras el campo tiene foco. |
data-filled | Presente cuando el campo tiene un valor. |
data-dirty | Presente una vez que el valor difiere del inicial. |
data-touched | Presente una vez que el campo ha recibido foco y lo ha perdido. |
| Prop | Tipo | Predeterminado |
|---|---|---|
align | "inline-start" | "inline-end" | "block-start" | "block-end" | "inline-start" |
separatorDibuja una línea fina entre un addon de bloque y el campo. | boolean | false |
| Atributo | Descripción |
|---|---|
data-slot="input-group-addon" | Selecciona los addons en CSS. |
data-align | La alineación actual. |
data-separator | Presente cuando se define separator. |
--input-group-addon-inset | Espacio entre el borde del grupo y un botón o tecla de su interior. |
| Prop | Tipo | Predeterminado |
|---|---|---|
variant | "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | "ghost" |
size | "xs" | "sm" | "icon-xs" | "icon-sm" | "xs" |
type | string | "button" |
feedbackTodas las props de Button funcionan, incluido el flujo de carga y éxito. | boolean | false |
| Atributo | Descripción |
|---|---|
data-slot="input-group-button" | Selecciona los botones de los addons en CSS. |
data-size | El tamaño actual. |
| Prop | Tipo | Predeterminado |
|---|---|---|
onClearSe llama después de borrar el campo. | () => void | – |
aria-label | string | "Clear" |
children | ReactNode | <IconX /> |
| Atributo | Descripción |
|---|---|
data-slot="input-group-clear" | Selecciona el botón de borrar en CSS. |
data-visible | Presente mientras el campo tiene un valor y es editable. |
| Prop | Tipo | Predeterminado |
|---|---|---|
revealedEstado controlado. Déjalo sin definir para que se gestione solo. | boolean | – |
onRevealedChange | (revealed: boolean) => void | – |
aria-label | string | "Show password" |
| Atributo | Descripción |
|---|---|
data-slot="input-group-password-toggle" | Selecciona el interruptor en CSS. |
data-revealed | Presente mientras se muestra la contraseña. |
| Prop | Tipo | Predeterminado |
|---|---|---|
thresholdCaracteres restantes a partir de los cuales el recuento empieza a destacar. | number | 10% of maxLength, at most 20 |
announcementMensaje para lectores de pantalla cuando el recuento cruza el umbral o llega al límite. | (remaining: number) => string | – |
| Atributo | Descripción |
|---|---|
data-slot="input-group-count" | Apunta al contador en CSS. |
data-state="near" | "limit" | Presente dentro del umbral y cuando no quedan caracteres. |
data-bump | Presente brevemente cuando se pulsa una tecla en el límite. |
| Atributo | Descripción |
|---|---|
data-slot="input-group-text" | Selecciona el texto de los addons en CSS. |
- ButtonBotones en todas las variantes y tamaños, con un flujo integrado de carga, éxito y error que omite el spinner en las peticiones rápidas.
- InputUn campo de texto con tres tamaños, estados inválido y de solo lectura, estilos de validación nativos y una fuente táctil de 16px para que los móviles nunca hagan zoom.
- MotionLas curvas de easing, duraciones y la comprobación de movimiento reducido con las que se anima cada componente, además de hooks para transformaciones de tamaño y resaltados deslizantes.
- useAutosizeHace crecer un textarea según lo que escribes entre su altura mínima y máxima, animando cada cambio sin tocar nunca el texto.
- useComposedRefMantiene una ref a tu propio elemento y sigue reenviándola a la ref que haya pasado el padre.
- useInvalidShakeSacude un control de formulario cuando un intento de envío lo encuentra inválido, y nunca mientras alguien aún está escribiendo.
Usado en bloques
Bloques que se construyen sobre Input group.
- BillingPlan y uso para un producto de IA, al estilo de Cursor, Claude y Vercel. Un medidor de uso dividido por modelo que proyecta el final del ciclo y avisa antes de que se agoten los créditos, un gráfico diario por el que puedes desplazarte, un límite de gasto con alertas que puedes previsualizar en el medidor, cambios de plan con prorrateo exacto, un formulario de tarjeta con validación real y facturas descargables en PDF.
- Diff ReviewRevisa las ediciones de un agente en todos los archivos antes de que se apliquen. Un árbol de archivos con contadores, aceptar o rechazar cada cambio, cada archivo o todo, comentarios en cualquier línea o rango que vuelven al agente, vistas unificada y dividida, resaltado a nivel de palabra, deshacer, ediciones en streaming y un resumen «Editados 4 archivos» para el chat.
- ModelsLa página Modelos de los ajustes de un producto de IA. Un modelo predeterminado con su contexto, velocidad y coste de un vistazo, un esfuerzo predeterminado que sabe qué admite cada modelo, una lista de modelos con búsqueda agrupada por proveedor con filtros, fijados e interruptores masivos, servidores compatibles con OpenAI con una prueba de conexión real y una actualización que te dice qué hay de nuevo.
- ProfileLa sección Perfil de los ajustes de un producto de IA. Recorta una foto en un círculo, elige un nombre de usuario que se comprueba mientras escribes, confirma un nuevo correo con un código de 6 dígitos, añade enlaces que reconocen el sitio y mira una tarjeta en vivo de cómo te ven los demás.