Input Group

Input Group

An input with attached addons – icons, text, buttons or keyboard hints – inline or as block rows. The whole group focuses as one control.

Playground

variant
size
leadingIcon
trailingButton
<InputGroup>
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="Search..." />
</InputGroup>

Preview

Wrap an InputGroupInput with addons aligned inline-start or inline-end; clicking an addon focuses the input.

<InputGroup>
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="Search..." />
</InputGroup>

Installation

npx shadcn@latest add https://espresso-base-ui.vercel.app/r/input-group.json

Usage

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from "@/components/ui/input-group"
<InputGroup variant="subtle" size="sm">
  <InputGroupAddon align="inline-start">
    <InputGroupText>https://</InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="example.com" />
</InputGroup>

Text and button addons

InputGroupText renders static prefixes or suffixes, while InputGroupButton is a compact ghost Button tuned to sit inside the group.

https://
<InputGroup>
  <InputGroupAddon align="inline-start">
    <InputGroupText>https://</InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="example.com" />
</InputGroup>

<InputGroup>
  <InputGroupInput placeholder="Enter value..." />
  <InputGroupAddon align="inline-end">
    <InputGroupButton size="xs">
      <Copy /> Copy
    </InputGroupButton>
  </InputGroupAddon>
</InputGroup>

<InputGroup>
  <InputGroupInput placeholder="Password" type="password" />
  <InputGroupAddon align="inline-end">
    <InputGroupButton size="icon-xs">
      <Eye />
    </InputGroupButton>
  </InputGroupAddon>
</InputGroup>

Variants and sizes

The group takes the same variant and size scales as Input – outline, subtle and ghost across xs to lg – and sizes the inner control to match.

<InputGroup variant="outline" size="md">
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="Outline (md)" />
</InputGroup>

<InputGroup variant="subtle" size="sm">
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="Subtle (sm)" />
</InputGroup>

<InputGroup variant="ghost" size="lg">
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="Ghost (lg)" />
</InputGroup>

Block alignment and textarea

block-start and block-end addons become full-width rows above or below the control, and the group switches to a column – swap in InputGroupTextarea for multi-line composers with an action row.

Label on top
<InputGroup>
  <InputGroupAddon align="block-start">
    <InputGroupText>Label on top</InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="Enter value..." />
</InputGroup>

<InputGroup>
  <InputGroupTextarea placeholder="Write a message..." rows={3} />
  <InputGroupAddon align="block-end">
    <InputGroupButton size="xs">Send</InputGroupButton>
  </InputGroupAddon>
</InputGroup>

States

Disable the inner control and the whole group follows; set data-valid, data-invalid or data-filled on the group to force those surfaces.

<InputGroup>
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput placeholder="Disabled" disabled />
</InputGroup>

<InputGroup data-invalid="true">
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput defaultValue="Invalid" />
</InputGroup>

<InputGroup data-valid="true">
  <InputGroupAddon align="inline-start">
    <InputGroupText>
      <Search />
    </InputGroupText>
  </InputGroupAddon>
  <InputGroupInput defaultValue="Valid" />
</InputGroup>

API reference

The group root carries the visual props; addons carry alignment; the input and textarea are restyled versions of Input and Textarea that drop their own borders and shadows so the group reads as one field.

InputGroup

PropTypeDefaultDescription
variant"outline" | "subtle" | "ghost""outline"Surface of the whole group – shadow ring, filled secondary background, or transparent until interaction.
size"xs" | "sm" | "md" | "lg""md"Group height (24 / 28 / 32 / 40px); the inner control inherits matching height, padding and text size via data-size.

InputGroupAddon

PropTypeDefaultDescription
align"inline-start" | "inline-end" | "block-start" | "block-end""inline-start"Placement – inline at either end of the control, or as a full-width row above (block-start) / below (block-end), which switches the group to a column layout.

InputGroupButton

PropTypeDefaultDescription
variantButton variant"ghost"Forwarded to the underlying Button.
size"xs" | "sm" | "icon-xs" | "icon-sm""xs"Compact sizes tuned for in-group use, including square icon-only options.
type"button" | "submit" | "reset""button"Button type – defaults to button to avoid accidental form submits.
ComponentDescription
InputGroupGroup root with role="group" (data-slot="input-group"); reflects data-variant and data-size and reacts to data-filled / data-invalid / data-valid.
InputGroupAddonAddon container (data-slot="input-group-addon", data-align). Clicking it focuses the input unless a button was clicked.
InputGroupTextMuted inline text or icon span for static prefixes and suffixes.
InputGroupButtonGhost Button preconfigured with compact in-group sizing.
InputGroupInputBorderless Input wired to the group (data-slot="input-group-control"); accepts all Input props except size.
InputGroupTextareaBorderless Textarea wired to the group (data-slot="input-group-control") for multi-line layouts.

Accessibility & styling hooks

The root and each addon render role="group", and the focus ring is drawn on the group when the inner data-slot="input-group-control" receives focus – so one visual control matches one focus target. Setting aria-invalid on the control adds a destructive ring to the whole group; label the control itself via htmlFor/id or a wrapping Field. The inputGroupVariants cva helper is also exported for custom composite fields.

Search documentation

Search the component docs