Skip to content

Input group

Puts icons, text or buttons inside an input or textarea.

Library
What’s this?

Installation

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

The CLI installs the Radix or Base UI file to match your project, with what it builds on: button, input, textarea.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • An input needs an icon inside it, like a search field with a magnifying glass.
  • An input needs fixed text before or after the value, like https:// or USD.
  • An input needs a button inside it, like clear, copy or show password.
  • A textarea needs a row of actions below the text, like a count and a send button.

Don't use it

  • A plain field with no icon, text or button. Use Input or Textarea.
  • People pick from suggestions as they type. Use Combobox, or Lookup for records.
  • Several separate controls sit side by side. Use Button group or a flex row.

Usage

InputGroup
├── InputGroupInput
└── InputGroupAddon
    ├── InputGroupText
    └── InputGroupButton

InputGroup is the field. Put an InputGroupInput or InputGroupTextarea in it, then the addons. An addon's align sets where it shows, before or after the control or on a row above or below it, whatever its place in the markup.

import { SearchIcon } from "lucide-react"

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group"
<InputGroup>
  <InputGroupInput type="search" aria-label="Search accounts" />
  <InputGroupAddon>
    <SearchIcon />
  </InputGroupAddon>
</InputGroup>

Clicking an addon outside a button focuses an InputGroupInput, but not a textarea. A click on a button keeps focus there, so a button that hides itself, like Clear shown only while there's text, should move focus back to the input first.

Examples

Text

Fixed text in an `InputGroupText` before and after the input.

https://
.com
$
USD

Buttons

A copy `InputGroupButton` with `size="icon-xs"` on a read-only link, and a text Apply button inside a promo code field.

Textarea

A count and a send button below an `InputGroupTextarea`, in an addon with `align="block-end"`.

0 of 280

States

One field with `aria-invalid` and an error, and one `disabled`, both set on `InputGroupInput`.

API reference

PartBuilt on
InputGroup<div> (opens in a new tab)
InputGroupAddon<div> (opens in a new tab)
InputGroupButtonButton
InputGroupText<span> (opens in a new tab)
InputGroupInput<input> (opens in a new tab)
InputGroupTextarea<textarea> (opens in a new tab)

InputGroupAddon

PropTypeDefault
align"inline-start" | "inline-end" | "block-start" | "block-end""inline-start"

InputGroupButton

PropTypeDefault
size"xs" | "sm" | "icon-xs" | "icon-sm""xs"
type"button" | "submit" | "reset""button"
variant"default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | "outline-brand" | "destructive-text" | "success" | "inverse""ghost"

SLDS mapping

InputGroup is an SLDS input with icons, slds-input-has-icon, with its frame and states read from the control inside. Addon icons are slds-input__icon, and InputGroupText is SLDS's fixed text, slds-form-element__addon.

Known gaps:

  • SLDS puts fixed text outside the field, beside it. Here it sits inside.
  • SLDS has no rows above or below a control. Block addons follow SLDS's spacing, not a blueprint.
PropValueSLDSNotes
InputGroupAddon aligninline-startslds-input-has-icon_leftIcons sit inside the field, as SLDS's do. Text sits inside too; SLDS's fixed text (slds-form-element__addon) sits outside.
InputGroupAddon aligninline-endslds-input-has-icon_rightIcon buttons sit inside, like SLDS's clear button (slds-input__icon_right).
InputGroupAddon alignblock-startnoneA row above the control. SLDS has none.
InputGroupAddon alignblock-endnoneA row below the control. SLDS has none.
InputGroupButton sizexsslds-button24px tall.
InputGroupButton sizesmslds-button28px tall.
InputGroupButton sizeicon-xsslds-button_icon slds-input__icon24px square, like SLDS's clear button.
InputGroupButton sizeicon-smslds-button_icon28px square.

Accessibility

  • Name the control with a visible label. Fixed text isn't part of its name, so repeat https:// or USD in the label or help text when people need it.
  • Give an icon button an aria-label, like "Clear".
  • Tab follows the markup, not align. A button in an inline-start or block-start addon placed after the control shows first but takes focus after it, so put that addon before the control.
  • InputGroupButton doesn't submit a form. Set type="submit" on the one that should.
  • In Cosmos dark, text in an invalid field is about 4.3:1 against its background, below AA. Salesforce's own stylesheet fails the same way.

Responsive behavior

  • The control takes the room left after the addons, so long fixed text squeezes the value. Set the width on the parent.

Rules

  • Use InputGroupInput and InputGroupTextarea inside a group, never Input or Textarea. The group reads its state from them.
  • Put icons straight into InputGroupAddon and actions into InputGroupButton. Wrap text in an InputGroupText inside the addon: bare text takes the icon color.
  • Set aria-invalid and disabled on the control, not on the group.