Components

Input Group

Composes an input with leading or trailing addons — icons, text labels, units, or buttons — inside a single bordered control that shares one focus ring.

Basic usage

Wrap an InputGroupInput with one or more InputGroupAddon slots. Clicking an addon focuses the input.

⌘K

Addon alignment

Use align="inline-start" for a leading prefix and align="inline-end" for a trailing suffix such as a unit. Alignment drives ordering with order-first/order-last, so the visual position does not depend on where you write the addon in the JSX.

USD

With a button

Place an InputGroupButton inside a trailing addon for an inline submit or action. It inherits every Button variant, but overrides size with its own compact scale and defaults type to button.

Textarea with a block addon

Swap the control for an InputGroupTextarea and the group drops its fixed height. Pair it with align="block-start" or align="block-end" to get a full-width toolbar row inside the same border — a compose box, in other words.

Markdown supported

Invalid state — two entry points

The group turns its border to destructive-ink from either place the aria-invalid flag can land. Set it on the control yourself and a has-[…] rule catches it. Render the group inside FormControl from @comitor/ui/form and Radix's Slot stamps the flag on the InputGroup element instead — never reaching the inner input, so a descendant selector alone would miss it. The component carries both rules, which is why a password field wrapped in a group goes red on validation failure just like the plain email field above it.

Parts and props

PropTypeDefaultDescription
InputGroupReact.ComponentProps<'div'>The container. Renders role="group", owns the border, height, focus ring, and the two error paths. Switches to h-auto when it holds a textarea or a block addon.
InputGroupInputReact.ComponentProps<'input'>An Input stripped of its own border, fill, and ring, tagged data-slot="input-group-control" so the group can see its focus and invalid state.
InputGroupTextareaReact.ComponentProps<'textarea'>The Textarea equivalent — resize-none, borderless, and it flips the group into the taller vertical layout.
InputGroupAddonReact.ComponentProps<'div'>A slot for icons, text, or buttons. Clicking it focuses the input, unless the click landed on a button.
InputGroupAddon.align'inline-start' | 'inline-end' | 'block-start' | 'block-end''inline-start'Where the addon sits. The inline values order it before/after the control on one row; the block values make the group a column and put the addon above/below.
InputGroupTextReact.ComponentProps<'span'>Muted, non-interactive text or icon label for use inside an addon (units, prefixes, hints).
InputGroupButton.variantButton variant — 'default' | 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link''ghost'Passed through to Button for an interactive addon (reveal, submit, clear).
InputGroupButton.size'xs' | 'sm' | 'icon-xs' | 'icon-sm''xs'Its own compact scale, narrower than Button.size, tuned to sit flush inside a 36px group.
InputGroupButton.type'button' | 'submit' | 'reset''button'Defaults to button so an addon action never submits the surrounding form by accident.
classNamestringAdditional Tailwind classes to merge on any part.

The two cva helpers behind the parts are public: inputGroupAddonVariants and inputGroupButtonVariants are exported from @comitor/ui. Call them when you need an element that is not an addon or a button to match the group's metrics exactly, rather than copying the class strings.

Accessibility

  • The control is a native <input> or <textarea>, so keyboard focus, typing, and form submission work without extra wiring.
  • The group exposes a shared focus-visible ring driven by the inner control — the entire field lights up on keyboard focus using the brand gold ring token, with the border going to --primary-ink.
  • The ring is drawn with no ring-offset, hugging the border exactly the way Input, Textarea and SelectTrigger do. An offset would paint a band of page background between border and ring, which reads as a dark halo on a card and makes one field in a form focus differently from its neighbours. Offsets belong to the non-text controls — Button, Checkbox, Switch, Tabs.
  • Set aria-invalid on the control or on the group — both switch the whole field to its destructive styling — and always pair the control with a visible <label> or aria-label.
  • Clicking anywhere in an addon focuses the input, giving icons and labels a larger, more forgiving hit target. This is a pointer convenience only; keyboard users tab straight to the control or the button, so the addon itself is never a tab stop.
  • Give icon-only InputGroupButton actions an aria-label so their purpose is announced.