Input Group
An input or textarea with addons — icons, text, buttons, kbd hints, spinners and dropdowns, aligned inline or block on one bordered surface.
- Status
- Since
0.10.0- Accessibility pattern
- native input with labelled addons
Last updated
Install
Add Input Group from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/input-groupThe same command installs the registry items it composes: @vegastack/button, @vegastack/input, @vegastack/textarea.
Usage
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
<InputGroup>
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>;Anatomy
Examples
Composition
InputGroup
├── InputGroupInput or InputGroupTextarea
├── InputGroupAddon
├── InputGroupButton
└── InputGroupTextAlign
align on InputGroupAddon takes inline-start (the default), inline-end, block-start and
block-end. Put the addon after the control in the DOM whatever the alignment — the focus
handling reads document order, and align only moves it visually.
The addon sits before the control.
The addon sits after the control.
A header above the control.
A footer below the control.
Icon
An icon addon sits on either edge, or on both.
Text
InputGroupText carries a prefix, a suffix or a counter.
Button
InputGroupButton is a Button sized for the field's interior. Give an icon-only one an
aria-label.
Kbd
A Kbd addon advertises the shortcut that focuses the field.
Dropdown
A DropdownMenu trigger rendered as an InputGroupButton.
Spinner
A Spinner addon reports work in flight without moving the control.
Textarea
InputGroupTextarea takes a header and a footer at once.
Custom Input
Put data-slot="input-group-control" on any control and the group picks up its focus and invalid
state.
RTL
Addon order is order-first/order-last over logical padding, so a right-to-left document mirrors
the whole group.
States
The group's rest, invalid and disabled chrome.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
align | "block-end" | "block-start" | "inline-end" | "inline-start" | — |
| Prop | Type | Default | Description |
|---|---|---|---|
loading | boolean | — | Shows a spinner over the label, blocks activation and sets aria-busy. The label keeps its
box at opacity: 0, so the button's width does not move and its accessible name survives
(API-5, A11Y-12). |
size | "icon-sm" | "icon-xs" | "sm" | "xs" | — | |
type | "button" | "reset" | "submit" | — | |
variant | "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | — |
InputGroup, InputGroupInput, InputGroupTextarea and InputGroupText add no props of their
own — each accepts everything the underlying element accepts.
Accessibility
- The group is a
role="group"; the control inside it keeps every native semantic, so labelling is the control's job, exactly as for a bareInput. - Clicking an addon focuses the control, unless the click landed on a button inside it.
- The group carries the focus affordance: a border tint while the control has focus (FOC-3), and no ring anywhere. The control itself is borderless, so nothing paints twice.
- Give every icon-only
InputGroupButtonanaria-labelor ansr-onlylabel; the design lint fails a nameless one.
| Contract | States tested |
|---|---|
| Behaviour | default, disabled, invalid |
| Accessibility | native-or-base-ui-semantics, browser-accessibility-test, grouped, invalid, disabled |
| Visual | default, focus, invalid, disabled |
Do / Don't
Deviations
Upstream's file plus packages/ui/upstream/patches/input-group.patch. Every hunk:
- FOC-1, FOC-3, FOC-6 — the group's
has-[[data-slot=input-group-control]:focus-visible]:border-ringplusring-3 ring-ring/50becomeshas-[[data-slot=input-group-control]:focus]:border-ring/70: a border tint on:focus, for mouse and keyboard alike, and no glow. Batch 4 added the same tint for[data-slot=command-input]— upstream'sCommandInputputs cmdk's input inside anInputGroupunder its own slot name, so the group never saw it focus. - FOC-5 — the invalid tint becomes
not-focus-within:has-[[data-slot][aria-invalid=true]]:border-destructive, so a focused group shows its focus cue rather than the error colour; thehas-aria-invalidhalo rings go with it. - FOC-6 —
InputGroupInputandInputGroupTextareadropring-0 focus-visible:ring-0 aria-invalid:ring-0, which existed only to cancel the ringInputandTextareano longer have. - DOC-2 —
cnis imported from@vegastack/design.