Skip to content

ComponentsForms

Input Group

Attach prefixes, suffixes, icons and buttons to a text input so they read as one field, such as an https:// prefix, a .com suffix, a currency, a copy button or a password toggle. Composes with React Aria's TextField, NumberField and SearchField, and the focus ring wraps the whole group.

React AriaSource
Shown on your public profile.

Installation

pnpm dlx shadcn@latest add @desyne/input-group

The CLI installs dependencies and any other components this one uses.

Usage

tsx
import { TextField } from "react-aria-components";
import { Label } from "@/components/ui/field";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group";
tsx
<TextField className="group/field flex flex-col gap-1.5">
  <Label>Website</Label>
  <InputGroup>
    <InputGroupAddon variant="segment">https://</InputGroupAddon>
    <InputGroupInput />
    <InputGroupAddon variant="segment">.com</InputGroupAddon>
  </InputGroup>
</TextField>

InputGroup is the same chrome as every other field (FieldGroup from field.tsx) with the padding moved onto its parts. Put it inside a React Aria field and InputGroupInput picks up the value, label, description and validation from the field.

TextField already has prefix and suffix

For a single icon or short text inside the field, Text Field's prefix and suffix props are enough. Reach for Input Group when you need flush segments, buttons, several addons, or a NumberField.

When to use

  • Input Group: fixed parts of a value (protocol, domain, unit, currency) or actions that act on the input (copy, reveal, clear, submit).
  • Text Field: a plain input, optionally with one icon or text adornment.
  • Search Field: search with a built-in clear button.
  • Number Field: numbers with stepper buttons.

Anatomy

tsx
<TextField>                         {/* or NumberField, SearchField */}
  <Label />
  <InputGroup>                      {/* role="group", draws the border and focus ring */}
    <InputGroupAddon />             {/* inline text or icon, or a flush segment */}
    <InputGroupInput />             {/* the <input> */}
    <InputGroupButton />            {/* ghost button inside, or a flush segment */}
  </InputGroup>
  <Description />
  <FieldError />
</TextField>
PartRendersNotes
InputGroupReact Aria Group (role="group")Field chrome with variant (outline, filled, underlined) and size. Shows the focus ring when anything inside has focus, and the invalid style when the field is invalid.
InputGroupInputReact Aria InputBare input that fills the space. Pads itself only at the group's edges.
InputGroupAddon<span>inline: muted text or icon inside the field. segment: a flush, tinted box divided by a border.
InputGroupButtonReact Aria Buttonghost (default): a small icon button inside the field. segment: a flush, full-height button divided by a border.

Examples

Icons and inline text

Inline addons sit inside the border, before or after the input: a search icon, a Kbd shortcut, a character count.

Buttons

InputGroupButton defaults to a 24px ghost icon button. Give icon-only buttons an aria-label. For a password toggle, switch the field's type.

Keep this secret. It has full access.

Segment button

variant="segment" turns the button into a flush, full-height part of the field, for actions like Subscribe or Apply. type="submit" submits the surrounding form.

Currency and units

The same parts work inside a React Aria NumberField: the input is formatted and parsed in the user's locale, and Up and Down arrows change the value.

$USD
Charged on the 1st of each month.
kg

Variants and sizes

variant and size work as on every other field. Underlined groups drop the side padding so content lines up with the underline.

Accessibility

  • The input gets its name from the surrounding field's Label (or aria-label on the field), and its description and error message through React Aria.
  • InputGroup is a React Aria Group with role="group". The focus ring is drawn on the group when the input or a button inside it has focus, so it always wraps the full field.
  • Addons are plain text. A screen reader user won't hear "https://" when focusing the input; if the prefix matters, mention it in the label or description ("Website, without https://").
  • Buttons are separate tab stops after (or before) the input. Icon-only buttons need an aria-label, and buttons that change state (copy, reveal) should update it ("Copied", "Hide password").

Styling

Data attributes

AttributeOnValues
data-slot="input-group"InputGroupAlways, plus React Aria's data-focus-within, data-hovered, data-invalid, data-disabled
data-slot="input-group-input"InputGroupInputAlways
data-slot="input-group-addon"InputGroupAddonAlways
data-slot="input-group-button"InputGroupButtonAlways
data-variantAddon, buttoninline · segment / ghost · segment

Padding

Parts read --ig-px for their edge padding: 8, 10 and 12px for sm, md and lg, and 0 for underlined groups. Override it on the group: className="[--ig-px:--spacing(4)]".

API Reference

InputGroup

Prop

Type

Also accepts every prop of React Aria's Group.

InputGroupAddon

Prop

Type

Also accepts every prop of <span>.

InputGroupButton

Prop

Type

Also accepts every prop of React Aria's Button, such as onPress and type.

InputGroupInput

Accepts every prop of React Aria's Input, such as placeholder.