Input Group
An Input Group displays icons, text, and Buttons inside the edge of an Input or a Textarea so that users see them as part of the field.
When to Use
- to mark a search box with an icon
- to show a fixed part of the value next to it, such as “USD” or “.example.com”
- for a Button that acts on the value, such as “Copy the payment link” or “Show the password”
- for a message box, with a Textarea and “Send” in a row under the text
- for a keyboard shortcut in an addon, with a Kbd
When Not to Use
- for a Button next to the Input and outside its edge (use an Input and a Button in a row)
- for a Button or text joined to the outside of an Input (use a Button Group)
- to filter a list by typing (use a Combobox, which is built on an Input Group)
- for the label above the control and the error under it (wrap the group in a Field)
Anatomy
| Element | Usage |
|---|---|
| Inline addon | Optional |
| Control | Required |
| Container | Required |
| Button | Optional |
| Block addon | Optional |
| Text | Optional |
Write the control first and its addons after it so that keyboard users reach the control with Tab before a Button in an addon. align sets the position of an addon, wherever it is in the markup.
| Align | Position |
|---|---|
inline-start | Before the control, on its line. The default. |
inline-end | After the control, on its line |
block-start | A row above the control |
block-end | A row below the control |
Sizes
An Input Group has no size prop. It grows when it has a block addon or a Textarea. InputGroupButton has sizes of its own.
| Size | Height | Placement |
|---|---|---|
xs | 24px | A text Button in an inline addon. The default. |
icon-xs | 24px | An icon-only Button in an inline addon |
sm | 32px | A text Button in a block addon |
icon-sm | 32px | An icon-only Button in a block addon |
sm and icon-sm are as tall as the group itself, so use them only in a block addon.
xs for a Button in an inline addon; it is 24px tall and fits inside the edge.sm is set in an inline addon, the Button is 32px tall and covers the top and bottom edges of the group.States
Set disabled and aria-invalid on the control. The group shows the states of its control.
Behavior
When users click an addon outside a Button, the group’s Input or Textarea receives focus.
Accessibility
The group and its addons are div elements with role="group" and no name. The control and the Buttons are native elements and keep their own keys.
FieldLabel, connect a Label to its id with htmlFor, or set aria-label. An icon or a unit in an addon doesn’t name the control.An icon-only InputGroupButton needs an aria-label and a Tooltip, as an icon-only Button does.
Installation
npx shadcn@latest add @summit/input-groupThe CLI also adds @summit/button, @summit/input, and @summit/textarea.
Usage
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from '@/components/ui/input-group';
<InputGroup>
<InputGroupInput aria-label="Amount" inputMode="decimal" placeholder="0.00" />
<InputGroupAddon align="inline-end">
<InputGroupText>USD</InputGroupText>
</InputGroupAddon>
</InputGroup>;With a Button
By default, InputGroupButton is a ghost Button at xs. For an icon-only Button, set size="icon-xs" and an aria-label.
import { CopyIcon } from '@phosphor-icons/react';
<InputGroup>
<InputGroupInput aria-label="Payment link" defaultValue="https://pay.example.com/inv-2026-014" readOnly />
<InputGroupAddon align="inline-end">
<InputGroupButton size="icon-xs" aria-label="Copy the payment link">
<CopyIcon />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>;With a Textarea
For a message box, use InputGroupTextarea and a block-end addon. To move a child and everything after it to the end of the row, add ms-auto to that child.
<InputGroup>
<InputGroupTextarea aria-label="Message" placeholder="Write a message to the client" />
<InputGroupAddon align="block-end">
<InputGroupText className="ms-auto text-xs">120 characters left</InputGroupText>
<InputGroupButton size="sm" variant="default">
Send
</InputGroupButton>
</InputGroupAddon>
</InputGroup>With a Label Inside
To name the control from inside the edge, place a Label in an addon and point its htmlFor at the control. The control then doesn’t need an aria-label.
import { Label } from '@/components/ui/label';
<InputGroup>
<InputGroupInput id="to" type="email" />
<InputGroupAddon>
<Label htmlFor="to">To</Label>
</InputGroupAddon>
</InputGroup>;API Reference
InputGroup finds its control by data-slot="input-group-control". If you build a control outside the registry, add that attribute so that the group shows its focus outline, invalid border, and disabled state.
InputGroupAddon
| Prop | Type | Default | Description |
|---|---|---|---|
align | 'inline-start' | 'inline-end' | 'block-start' | 'block-end' | 'inline-start' | The side of the control where the addon is placed |
InputGroupButton
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'xs' | 'sm' | 'icon-xs' | 'icon-sm' | 'xs' | The height and the padding |
variant | A variant of a Button | 'ghost' | The fill and the text color |
type | 'button' | 'submit' | 'reset' | 'button' | The type attribute of the button element |
Other props of InputGroupButton are passed to the Button that it renders.