Input Group

Icons, prefixes, suffixes, and actions inside one shared control boundary.

Preview

Use Input Group when a control needs an icon, unit, prefix, or related action inside its border. Keep its visible label, description, and error in Field, with the same 4px gap and caption-sized supporting text as Input.

Type “website” to filter the four sample projects locally.

Search four sample projects. No data leaves this page.

Installation

pnpm dlx shadcn@latest add http://127.0.0.1:3017/r/input-group.json

Installs Input Group and its Migoy Input, Textarea, and Button dependencies. Review any existing customizations to those components before accepting updates. It reuses your cn utility and semantic theme and does not change global CSS. Field, icons, and Spinner are separate items used in these examples. See the registry guide for local endpoints and updates.

Composition

import {
  InputGroup, InputGroupAddon, InputGroupButton,
  InputGroupInput, InputGroupText, InputGroupTextarea,
} from '@/components/ui/input-group';
PartResponsibility
InputGroupShared border, surface, size, and focus outline
InputGroupInputMigoy Input with its individual border and outline removed
InputGroupTextareaMigoy Textarea styled for the shared boundary
InputGroupAddonIcon, text, or action region, positioned by align
InputGroupTextMuted inline text such as a prefix or unit
InputGroupButtonMigoy Button sized to fit inside the group

Place the control first and addons after it in DOM order. align determines their visual position. Clicking a non-interactive addon focuses the input or textarea; clicking its button or link keeps that action's behavior. Place only one editing control in each group. Use FieldGroup for multiple fields.

Prefixes and suffixes

inline-start is the default addon position; use inline-end for trailing content. Logical alignment follows the reading direction. Decorative icons sit 8px from their outer edge, with 8px between the addon and input text. Text-only outer edges use the size’s standard padding: 8px small, 12px default, and 16px large. Each side is handled independently. Embedded buttons retain their own padding and focus treatment.

Enter the host only. The prefix is visual; it is not part of the input value.

Prefixes and suffixes are visual content, not part of the submitted value. Include units and meaning in the accessible label or description, then hide duplicate decoration with aria-hidden. A visual https:// prefix does not turn a host-only value into a valid type="url" value; use a text input and assemble or validate the URL in your application.

Actions

Try the password toggle and clear button. Actions remain keyboard-focusable and have their own visible focus treatment. InputGroupButton defaults to type="button", so it does not accidentally submit a surrounding form.

Name icon-only actions independently. Toggle actions use a stable name plus aria-pressed. The clear action returns focus to the input after clearing its value. For a submit action, set type="submit" explicitly. Button variants such as ghost, secondary, and default are available; the group determines the embedded button's height.

Sizes

Set controlSize on the group, not its input. Heights and radii follow the same shared tokens and fallbacks as Input and Button.

SizeGroup heightRadius
sm24px3px
default32px4px
lg44px6px

Small editing controls use 12px / 16px text on desktop; their addons and embedded button text also use the caption role. Editing controls retain 16px text on smaller screens. Labels remain 14px / 20px. Icons and spinners are 14px in small groups and 16px in default and large groups; embedded button icons inherit the group scale. Small is for dense desktop layouts. Prefer large controls for touch; keep important actions outside the group if they need larger targets. Grouped textareas and block addons grow vertically instead of using a fixed height. Long input values scroll within the control.

Interaction states

The group is flat and shadow-free. Keyboard focus on the editing control outlines the entire group; embedded actions retain their own outline. Hover and focus examples below are pinned with the same utilities as the live interactions.

Static loading example; no request is running.

Set aria-invalid on the editing control to give the shared border and focus outline the destructive color. Keep the label neutral and explain the error below the group with FieldError. Connect descriptions and errors using aria-describedby.

<Field data-invalid>
  <FieldLabel htmlFor="email">Email address</FieldLabel>
  <InputGroup>
    <InputGroupInput id="email" aria-invalid aria-describedby="email-error" />
    <InputGroupAddon><Mail aria-hidden="true" /></InputGroupAddon>
  </InputGroup>
  <FieldError id="email-error">This email address is already in use.</FieldError>
</Field>

disabled and readOnly belong on the input or textarea. A disabled editing control dims the group. Disable embedded actions explicitly too; a disabled input does not disable sibling buttons. For pending work, use a visible status and aria-busy; the loading example is static and sends no request.

Block addons and textarea

Use block-start or block-end to place supporting content above or below the editing area. Block addons stack the group vertically; use them together or separately, without mixing inline addons in the same group.

Team workspace

0 of 280 characters. This demo does not save your note.

The grouped textarea is vertically resizable and supports native textarea props, including rows, maxLength, and ref. It now composes Migoy Textarea, installed as a registry dependency. Both versions share their base editing styles; the group owns the border, outline, radius, and padding. Its visible Field label remains outside the group; block content supplements that label.

Accessibility

  • Keep the editing control labeled with htmlFor and a matching ID.
  • Decorative icons use aria-hidden="true"; they are not extra Tab stops.
  • Tab reaches the input first, then interactive addons in DOM order. Shift+Tab reverses that order.
  • Preserve native editing keys. Buttons support Enter and Space.
  • Keep helper and error text outside the group and explicitly associated with the control.
  • Use prefixes and units that fit the available width; keep longer explanations in FieldDescription.

See the Icons foundation for the shared size scale and icon accessibility guidelines.

API

PartProps
InputGroupNative div props, controlSize: sm, default (default), lg
InputGroupAddonNative div props, align: inline-start (default), inline-end, block-start, block-end
InputGroupInputNative input props; sizing comes from the group
InputGroupTextareaNative textarea props
InputGroupButtonButton props except size; defaults to variant="ghost", type="button"
InputGroupTextNative span props

Based on shadcn Input Group, adapted to Migoy's native Input, Button, and shared control tokens.

On this page