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
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Input Group before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify input-groupInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/input-groupRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The 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.
Textarea with an inline addon
Beside a textarea, an inline-start or inline-end addon sits at the top, level with the
first line, rather than at the textarea's middle — the corner a status icon such as
AutoSaveIndicator variant="icon" belongs in (FRM-16).
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. Click or Tab into a field to see the focus state: the
group's border darkens subtly (border-ring/50) over 150ms and nothing fills; the invalid group
keeps its destructive border while focused.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
align | "block-end" | "block-start" | "inline-end" | "inline-start" | null | — |
| 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" | null | — | |
type | "button" | "reset" | "submit" | — | |
variant | "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | — |
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: while its input or textarea has focus, the group's
border darkens subtly (
border-ring/50, easing in over 150ms) — text entry's exception to FOC-14 (MK 2026-09-27). No background changes, on the group or on the control inside it (which has no border and no fill), and there is no ring anywhere. An invalid group keeps its destructive border while focused. An addon button is a button: its own focus keeps the global background tint. - Under
forced-colors: activethe group (data-field-group) carries the outline and the control inside stands down, so the two never paint a double ring (FOC-7). - 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/50is removed, and the group gainsdata-field-group, which the forced-colours fallback keys on. There is no ring and no glow. - FRM-16 — the
inline-startandinline-endaddons addgroup-has-[>textarea]/input-group:self-startandpt-2.5, so beside a textarea they sit level with the first line instead of the textarea's middle. - FOC-14 — the invalid border is
has-[[data-slot][aria-invalid=true]]:border-destructive, held in every state, focused or not; thehas-aria-invalidhalo rings go with it. - FOC-14 text-entry exception (MK 2026-09-27) — the group adds
has-[input:focus,textarea:focus]:not-has-aria-invalid:border-ring/50withtransition-[color,background-color,border-color] duration-150 ease-out: a subtle darker border on the GROUP that eases in while its control has focus —[data-slot=command-input]and the popup search rows included — and no fill anywhere (base.cssexcludes text inputs andtextareafrom the background tint). Thenot-has-aria-invalidguard keeps the destructive border; an addon button's focus keeps the tint. - FOC-6 —
InputGroupInputandInputGroupTextareadropring-0 focus-visible:ring-0 aria-invalid:ring-0, which existed only to cancel the ringInputandTextareano longer have. - API-26 — NO HUNK:
InputGroupInputrendersInputandInputGroupTextarearendersTextarea, both Base UIField.Controls, so inside aFieldthey take their label, description and error ids andaria-invalidfrom it. - DOC-2 —
cnis imported from@vegastack/design. - FOC-16 — text entry shows hover as a darker
input-hoverborder, focus as thering/50border alone (no ring or glow; an invalid field keeps only its destructive border), and read-only as a faintreadonlyfill on a transparent border. Operator, Regent, 2026-10-31; halo removed 2026-11-01.