Gesso
Forms

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>
.tech

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.

PropTypeDefaultWhat 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 whose htmlFor matches the InputGroupInput's id, or with aria-label when 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 an id and list it in the input's aria-describedby, or put it in the label.
  • aria-invalid turns the input's border border-destructive and makes a screen reader announce it as invalid, and the addon keeps its color. Say what is wrong in a FieldError listed in aria-describedby.
  • disabled takes 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 CommandInput draws its own search glass and has leading and trailing slots.

Install

shadcn add @gesso/input-group

On this page