Input group
Kit Input with a leading or trailing addon overlaid on the field.
Input group puts an icon or a short affix inside an Input,
like a search glass at the start or a domain at the end. The addon is laid over the field
rather than beside it, and the input pads the side the addon sits on, so typed text does not
run under it. InputGroupInput is an Input and takes every Input prop, so it is labelled,
marked invalid, and disabled the same way.
Usage
import { MagnifyingGlass } from '@phosphor-icons/react';
import { Field, FieldLabel } from '@/components/ui/field';
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from '@/components/ui/input-group';
<Field>
<FieldLabel htmlFor="search">Search</FieldLabel>
<InputGroup>
<InputGroupInput id="search" type="search" placeholder="Find a file" />
<InputGroupAddon>
<MagnifyingGlass className="size-4" />
</InputGroupAddon>
</InputGroup>
</Field>Put the addon after the input in the markup. The input pads itself for an addon in either
order, but the addon only dims with a disabled input when it comes after it. The addon is
text-fg-muted and centered on the field's height.
Start or end
align="inline-start", the default, sets the addon 12px in from the start edge, and
align="inline-end" sets it 12px in from the end. Both follow the writing direction, so on
a right-to-left page a start addon sits on the right and the input pads that side.
The input pads that side to 36px, which leaves room for a 16px icon. Text like .tech or a
currency code is wider, so give the input a wider padding on that side with a plain class
like pe-14, which takes the place of the group's.
<InputGroup>
<InputGroupInput
id="workspace"
aria-describedby="workspace-suffix"
className="pe-14"
/>
<InputGroupAddon id="workspace-suffix" align="inline-end">
.tech
</InputGroupAddon>
</InputGroup>Addons let clicks through
An addon is pointer-events-none, so a click on the icon lands on the input and focuses
it. For a button in the addon, like one that clears the field or shows a password, add
pointer-events-auto to the addon so the button takes the click, give the button an
aria-label, and pad the input for the button's width. The button comes after the input in
the tab order.
Props
InputGroup and InputGroupAddon take <div> attributes, and InputGroupInput takes
every Input prop, plus this.
| Prop | Type | Default | What it does |
|---|---|---|---|
align | "inline-start" | "inline-end" | "inline-start" | On InputGroupAddon. Sets the addon 12px in from the start or end edge, and pads the input on that side to 36px. |
The InputGroupAddonAlign type is exported for code that passes align along.
Accessibility
- The input is a native
<input>and the group is a plain<div>with no role. Name the input with a FieldLabel whosehtmlFormatches theInputGroupInput'sid, or witharia-labelwhen there is no visible label. A placeholder is not a label. - The addon is not part of the input's name or description. A Phosphor icon has no name
unless you pass
alt, so it adds nothing. When a text affix matters, like a unit or a domain, give the addon anidand list it in the input'saria-describedby, or put it in the label. aria-invalidturns the input's borderborder-destructiveand makes a screen reader announce it as invalid, and the addon keeps its color. Say what is wrong in a FieldError listed inaria-describedby.disabledtakes the input out of the tab order at 64% opacity, and an addon placed after the input dims with it.- Focus, forced colors, and size are Input's: a 2px ring in the contrast ink on
:focus-visible, a transparent outline that forced colors paints, and 40px tall at every pointer size. - A button in an addon brings its own target size, and nothing in the group makes it larger.
When to use something else
- Input when nothing sits inside the field.
- Combobox when the search picks one value from a list.
- Command when typing filters a list of actions. Its
CommandInputdraws its own search glass and hasleadingandtrailingslots.
Install
shadcn add @gesso/input-group