Input Group
Compound input shell that composes Input with icons, addons, buttons, and selects inside a single bordered row.
Description
InputGroup is a compound primitive. The root InputGroup wraps InputGroupProvider + InputGroupShell; inside the shell you arrange InputGroupLeadingIcon, InputGroupAddon, InputGroupSlot (which holds the actual InputGroupControl plus optional inline icons or InputGroupKbd), InputGroupButton, and InputGroupSelect. Every sub-part reads size, invalid, disabled, and loading from context, so wrapping the group in a Field is enough to cascade state across the whole row.
Reach for InputGroup whenever a single text value needs visible chrome beyond a plain Input: search bars with a ⌘K hint and a submit button, URL fields with a https:// prefix and a copy button, currency amounts with a trailing picker, phone fields with a country flag select, credit-card numbers with brand badges, or password fields with a built-in show/hide toggle.
For plain single-value text capture without affordances, use Input directly; InputGroup adds a shell wrapper you don't need. For OTP or one-character-per-slot input, reach for InputOTP. For numeric increment/decrement controls, use InputStepper. For multi-control rows that are not a single field (e.g. an address row), use a FieldGroup with multiple Field instances instead.
Installation
Anatomy
Usage
Examples
Sizes
Three sizes (xs, sm, md (default)) match the surrounding type scale and resize the shell radius, padding, and inner controls. When InputGroup sits inside a Field, Field's size is authoritative and cascades through context; set size on the group only for standalone use.
States
Field propagates invalid, disabled, and loading to every sub-part. invalid swaps the border, outline, and addon tones to error; disabled mutes the row; loading paints the info-tinted outline, drops a spinner into the slot, and disables trailing actions.
With Leading Icon
InputGroupLeadingIcon paints a fixed-square bordered cell (bg-weakest) for an icon or short symbol on the left of the row. Use it when you want the icon to read as part of the chrome rather than as content inside the input.
With Addon
InputGroupAddon is a bordered prefix or suffix for short non-editable copy like https:// or units. Combine it with a trailing InputGroupButton (iconOnly) for copy / share affordances.
With Button
InputGroupButton wraps Button and locks its size to the group's size scale. Drop it after the slot for a trailing submit / action, or use iconOnly for a compact icon affordance.
With Select
InputGroupSelect wraps Select in the compact variant and inherits group context. Reach for it when the trailing affordance is a picker: currency, country, permission scope, or domain suffix.
With Kbd
Nest InputGroupKbd inside InputGroupSlot, right of the control, to surface a keyboard shortcut hint (⌘K, /, etc.). The Kbd rescales padding and text with the group's size.
Password
<InputGroupControl type="password" /> ships an automatic show/hide eye toggle with no extra wiring required. The toggle disables itself when the group is disabled or loading and exposes aria-pressed for assistive tech.
Credit Card
CreditCardInput is a self-contained field that owns its InputGroup, masks the digits (auto-switching between the default 4-4-4-4 grid and Amex's 4-6-5 layout), detects the brand for the trailing badge, and shows a ✓ / ✗ validity marker. It emits the raw (unmasked) digits plus { cardType, isValid, isPotentiallyValid } through onValueChange. Drop it inside a Field and the size / invalid / disabled / loading cascade applies automatically.
Install it from the registry. The command also pulls in input-group and the useCreditCardInput hook for you:
Need full control over the chrome? Compose the parts by hand with the underlying useCreditCardInput hook (add use-credit-card-input).
Date
DateInput owns its InputGroup with a leading calendar icon and masks a fixed DD / MM / YYYY entry (configurable to MDY or YMD via order). It emits a parsed ISO string plus a Date object through onValueChange.
Install it from the registry. The command also pulls in input-group and the useDateInput hook for you:
Phone Number
PhoneInput owns the country picker (an InputGroupSelect), formats national-format digits for the selected country via libphonenumber-js, and renders the dial code as a static label in front of the input. It emits a submit-ready E.164 string (e.g. "+15551234567", or "" when empty) plus { country, dialCode, nationalNumber } through onValueChange, re-firing when the country changes, so no manual dialCode + digits assembly is needed.
Install it from the registry. The command also pulls in input-group, select, country-flag, and the usePhoneInput hook for you:
Currency Amount
For currency entry, compose an InputGroupLeadingIcon (the symbol) with a trailing InputGroupSelect that owns the ISO code. The select's prefix slot holds the flag glyph and valueChildren overrides the trigger label so the closed state stays compact.
Composition
InputGroup is a convenience wrapper over InputGroupProvider + InputGroupShell. Render the two explicitly when you need to branch logic between the context and the chrome, or wrap a non-standard outer container around the shell.
Accessibility
InputGroup keeps native input semantics: the inner <input> carries textbox role, focus order, and IME or autofill behavior. Wrap the group in a Field + FieldLabel to give it an accessible name; the label's htmlFor ties to the control's id.
ARIA notes:
InputGroupShellrenders withrole="group"and setsaria-busywhileloadingis true.aria-invalidis set onInputGroupControlautomatically whenField invalidis in effect; pair it with aFieldErrorso the message is announced.- The control inherits its accessible name from
FieldLabel,aria-label, oraria-labelledby; never rely on the placeholder alone. - The built-in password toggle uses
aria-label="Show password" / "Hide password"and togglesaria-pressedautomatically. - While
loading, the shell setspointer-events-noneand trailing buttons disable themselves to prevent submits mid-flight.
Styling
Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):
Data slots and attributes: the family sets these for CSS targeting:
data-slot="input-group"on the shell wrapper, withdata-size="<size>", plus the boolean attributesdata-invalid,data-disabled,data-loading(each present only whentrue).data-slot="input-group-slot"on each slot wrapper.data-slot="input-group-control"on the inner<input>.data-slot="input-group-leading-icon"on bordered leading icon cells.data-slot="input-group-addon"on bordered text addons.data-slot="input-group-toolbar"on toolbar rows, withdata-position="<start | end | between>".data-slot="input-group-button"on trailing action buttons.data-slot="input-group-select"on the wrapped trailing select trigger.data-slot="input-group-kbd"on keyboard shortcut hints.data-slot="input-group-helper-icon"on inline status icons.data-slot="input-group-card"on credit-card brand cells (withdata-loadingwhentrue).data-slot="input-group-password-toggle"on the auto-rendered password show/hide button (witharia-pressed).
Target a specific state in CSS:
Related Components
- Input: single-element primitive without surrounding chrome; reach for it when no affordances are needed.
- Field: wraps
InputGroupfor accessible label, description, and error wiring, plus thesize/invalid/disabled/loadingcascade. - InputOTP: one-character-per-slot OTP field; a distinct shape from
InputGroup. - InputStepper: numeric value with bracketed −/+ buttons; reach for it when the affordance is increment or decrement.
- Textarea: multi-line plain field with no chrome; reach for it when you don't need the shell.
API Reference
InputGroup
Compound shell entry point. Wraps InputGroupProvider + InputGroupShell and accepts any standard div attribute. Extends React.ComponentProps<"div">.
Props
Variants
InputGroupSlot
Flexible inner row that holds the InputGroupControl plus optional inline icons or InputGroupKbd. Reads size / invalid / disabled / loading from context. Extends React.ComponentProps<"div">.
Props
InputGroupControl
The actual input element. Wraps Input and renders a built-in show/hide toggle when type="password". Extends React.ComponentProps<typeof Input> (without size, which is read from context).
Props
InputGroupLeadingIcon
Fixed-square bordered slot painted with --color-weakest for an icon or short symbol on the leading edge of the row. Extends React.ComponentProps<"div">.
Props
InputGroupAddon
Bordered prefix or suffix slot for short non-editable copy like https:// or units. Extends React.ComponentProps<"div">.
Props
InputGroupButton
Trailing action button. Wraps Button, locks size to the group's size scale (xs → md, sm → lg, md → xl), and disables itself while the group is invalid / disabled / loading. Extends React.ComponentProps<typeof Button> (without size).
Props
InputGroupSelect
Trailing dropdown affordance. Wraps Select in variant="compact" and inherits size / invalid / disabled from context. Extends React.ComponentProps<typeof Select> plus presentation props.
Props
InputGroupKbd
Inline keyboard shortcut badge. Renders a neutral soft Badge sized to match the group. Extends React.ComponentProps<typeof Badge>.
Props
InputGroupProvider
Broadcasts size, invalid, disabled, and loading down to nested group parts. Most apps inherit these from Field via InputGroup and never render InputGroupProvider directly. (advanced)
Props
InputGroupShell
Visual shell painted by InputGroup. Render it directly only when composing InputGroupProvider with a non-standard outer chrome. Extends React.ComponentProps<"div">. (advanced)
Props
InputGroupToolbar
Horizontal toolbar row inside the shell with configurable justification. Extends React.ComponentProps<"div">. (advanced)
Props
InputGroupCard
Fixed-aspect card slot for trailing brand badges, typically a credit-card brand mark. Extends React.ComponentProps<"div">. (advanced)
Props
InputGroupHelperIcon
Small status icon shown inline inside an InputGroupSlot (e.g. ✓ / ✗ / ?). Colours track group context: text-body by default, text-disabled while disabled or loading, and text-error-base when invalid, where the icon also swaps to a warning glyph. Extends React.ComponentProps<"span">. (advanced)
Props
InputGroupField
Convenience wrapper that composes Field + FieldLabel + FieldDescription + FieldError around an InputGroup. Reach for it when you don't need to customise the surrounding Field. Extends React.ComponentProps<"div">. (advanced)