Embossed UI

Search documentation

Search for a page or component.

Input Group

Add addons, buttons, and helper content to inputs.

import { MailIcon, SearchIcon } from "lucide-react"

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group"

export function InputGroupDemo() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <InputGroup className="max-w-xs">
        <InputGroupInput
          placeholder="Quick actions"
          aria-label="Quick actions"
        />
        <InputGroupAddon>
          <SearchIcon />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup className="max-w-xs">
        <InputGroupInput
          type="email"
          placeholder="you@studio.com"
          aria-label="Email"
        />
        <InputGroupAddon>
          <MailIcon />
        </InputGroupAddon>
      </InputGroup>
    </div>
  )
}

Installation

pnpm dlx shadcn@latest add https://ui.ikramhasan.com/r/input-group.json

Usage

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from "@/components/ui/input-group"
<InputGroup>
  <InputGroupInput placeholder="Search..." />
  <InputGroupAddon>
    <SearchIcon />
  </InputGroupAddon>
</InputGroup>

Examples

Text

https://
.myshopify.com
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
  InputGroupText,
} from "@/components/ui/input-group"

export function InputGroupTextExample() {
  return (
    <InputGroup className="max-w-xs">
      <InputGroupInput placeholder="acme" aria-label="Store" />
      <InputGroupAddon>
        <InputGroupText>https://</InputGroupText>
      </InputGroupAddon>
      <InputGroupAddon align="inline-end">
        <InputGroupText>.myshopify.com</InputGroupText>
      </InputGroupAddon>
    </InputGroup>
  )
}

Button

import { CopyIcon } from "lucide-react"

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group"

export function InputGroupButtonExample() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <InputGroup className="max-w-xs">
        <InputGroupInput
          defaultValue="sk_live_51H8…"
          readOnly
          aria-label="API key"
        />
        <InputGroupAddon align="inline-end">
          <InputGroupButton size="icon-xs" aria-label="Copy">
            <CopyIcon />
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup className="max-w-xs">
        <InputGroupInput
          placeholder="Invite by email"
          aria-label="Invite by email"
        />
        <InputGroupAddon align="inline-end">
          <InputGroupButton size="sm" variant="secondary">
            Invite
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
    </div>
  )
}

Textarea

0 / 2000
import { ArrowUpIcon, PlusIcon } from "lucide-react"

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupText,
  InputGroupTextarea,
} from "@/components/ui/input-group"

export function InputGroupTextareaExample() {
  return (
    <InputGroup className="max-w-md">
      <InputGroupTextarea placeholder="Ask anything…" aria-label="Message" />
      <InputGroupAddon align="block-end">
        <InputGroupButton
          size="icon-xs"
          variant="secondary"
          aria-label="Attach"
        >
          <PlusIcon />
        </InputGroupButton>
        <InputGroupText className="ml-auto">0 / 2000</InputGroupText>
        <InputGroupButton
          size="icon-xs"
          variant="default"
          className="rounded-full"
          aria-label="Send"
        >
          <ArrowUpIcon />
        </InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
  )
}

Invalid and disabled

import { MailIcon, SearchIcon } from "lucide-react"

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group"

export function InputGroupInvalid() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <InputGroup className="max-w-xs">
        <InputGroupInput
          defaultValue="not-an-email"
          aria-invalid
          aria-label="Email"
        />
        <InputGroupAddon>
          <MailIcon />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup className="max-w-xs" data-disabled>
        <InputGroupInput
          disabled
          placeholder="Disabled"
          aria-label="Disabled"
        />
        <InputGroupAddon>
          <SearchIcon />
        </InputGroupAddon>
      </InputGroup>
    </div>
  )
}

API Reference

InputGroup

The main component that wraps inputs and addons.

PropTypeDefault
classNamestring
<InputGroup>
  <InputGroupInput />
  <InputGroupAddon />
</InputGroup>

InputGroupAddon

Displays icons, text, buttons, or other content alongside inputs.

PropTypeDefault
align"inline-start" | "inline-end" | "block-start" | "block-end""inline-start"
classNamestring
<InputGroupAddon align="inline-end">
  <SearchIcon />
</InputGroupAddon>

For <InputGroupInput />, use the inline-start or inline-end alignment. For <InputGroupTextarea />, use the block-start or block-end alignment.

The InputGroupAddon component can have multiple InputGroupButton components and icons.

<InputGroupAddon>
  <InputGroupButton>Button</InputGroupButton>
  <InputGroupButton>Button</InputGroupButton>
</InputGroupAddon>

InputGroupButton

Displays buttons within input groups.

PropTypeDefault
size"xs" | "icon-xs" | "sm" | "icon-sm""xs"
variant"default" | "destructive" | "outline" | "secondary" | "ghost" | "link""ghost"
classNamestring
<InputGroupButton>Button</InputGroupButton>
<InputGroupButton size="icon-xs" aria-label="Copy">
  <CopyIcon />
</InputGroupButton>

InputGroupInput

Replacement for <Input /> when building input groups. This component has the input group styles pre-applied and uses the unified data-slot="input-group-control" for focus state handling.

PropTypeDefault
classNamestring

All other props are passed through to the underlying <Input /> component.

<InputGroup>
  <InputGroupInput placeholder="Enter text..." />
  <InputGroupAddon>
    <SearchIcon />
  </InputGroupAddon>
</InputGroup>

InputGroupTextarea

Replacement for <Textarea /> when building input groups. This component has the textarea group styles pre-applied and uses the unified data-slot="input-group-control" for focus state handling.

PropTypeDefault
classNamestring

All other props are passed through to the underlying <Textarea /> component.

<InputGroup>
  <InputGroupTextarea placeholder="Enter message..." />
  <InputGroupAddon align="block-end">
    <InputGroupButton>Send</InputGroupButton>
  </InputGroupAddon>
</InputGroup>