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.
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.
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.
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
| Prop | Type | Default | Description |
|---|---|---|---|
InputGroup | React.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. |
InputGroupInput | React.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. |
InputGroupTextarea | React.ComponentProps<'textarea'> | — | The Textarea equivalent — resize-none, borderless, and it flips the group into the taller vertical layout. |
InputGroupAddon | React.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. |
InputGroupText | React.ComponentProps<'span'> | — | Muted, non-interactive text or icon label for use inside an addon (units, prefixes, hints). |
InputGroupButton.variant | Button 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. |
className | string | — | Additional 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-visiblering 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 wayInput,TextareaandSelectTriggerdo. 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-invalidon the control or on the group — both switch the whole field to its destructive styling — and always pair the control with a visible<label>oraria-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
InputGroupButtonactions anaria-labelso their purpose is announced.