Input group
An input with icons, text, keyboard hints or buttons attached inside the same border.
npx prodular-ui add input-groupIcons, prefixes and suffixes
Lowercase letters, numbers and dashes.
import { SearchIcon } from "lucide-react"
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@/components/ui/input-group"import { Kbd } from "@/components/ui/kbd"
export default function InputGroupDefault() { return ( <div className="flex w-full max-w-sm flex-col gap-6"> <InputGroup> <InputGroupAddon> <SearchIcon /> </InputGroupAddon> <InputGroupInput placeholder="Search customers" aria-label="Search customers" /> <InputGroupAddon align="inline-end"> <Kbd>/</Kbd> </InputGroupAddon> </InputGroup> <Field> <FieldLabel>Workspace URL</FieldLabel> <InputGroup> <InputGroupAddon> <InputGroupText>tidewater.app/</InputGroupText> </InputGroupAddon> <InputGroupInput defaultValue="northbeam" className="pl-0!" /> </InputGroup> <FieldDescription>Lowercase letters, numbers and dashes.</FieldDescription> </Field> <InputGroup> <InputGroupAddon> <InputGroupText>$</InputGroupText> </InputGroupAddon> <InputGroupInput defaultValue="4,800" className="tabular-nums" aria-label="Monthly budget" /> <InputGroupAddon align="inline-end"> <InputGroupText>USD per month</InputGroupText> </InputGroupAddon> </InputGroup> </div> )}With buttons
Copy and reveal actions for links and secrets.
"use client"
import * as React from "react"import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "lucide-react"
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group"
export default function InputGroupActions() { const [visible, setVisible] = React.useState(false) const [copied, setCopied] = React.useState(false) return ( <div className="flex w-full max-w-sm flex-col gap-4"> <InputGroup> <InputGroupInput readOnly defaultValue="https://tidewater.app/invite/k7Qm2x" aria-label="Invite link" className="font-mono text-[13px]" /> <InputGroupAddon align="inline-end"> <InputGroupButton size="icon-xs" aria-label={copied ? "Copied" : "Copy invite link"} onClick={() => { setCopied(true) setTimeout(() => setCopied(false), 1600) }} > {copied ? <CheckIcon /> : <CopyIcon />} </InputGroupButton> </InputGroupAddon> </InputGroup> <InputGroup> <InputGroupInput type={visible ? "text" : "password"} defaultValue="sk_live_51Hq8a2" aria-label="Webhook secret" className="font-mono text-[13px]" /> <InputGroupAddon align="inline-end"> <InputGroupButton size="icon-xs" aria-label={visible ? "Hide secret" : "Show secret"} onClick={() => setVisible((v) => !v)} > {visible ? <EyeOffIcon /> : <EyeIcon />} </InputGroupButton> </InputGroupAddon> </InputGroup> </div> )}Sizes
import { SearchIcon } from "lucide-react"
import { Button } from "@/components/ui/button"import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group"
export default function InputGroupSizes() { return ( <div className="flex w-full max-w-md flex-col gap-3"> {(["sm", "default", "lg"] as const).map((size) => ( <div key={size} className="flex items-center gap-2"> <InputGroup size={size}> <InputGroupAddon> <SearchIcon /> </InputGroupAddon> <InputGroupInput placeholder="Search invoices" aria-label="Search invoices" /> </InputGroup> <Button size={size} variant="outline"> Search </Button> </div> ))} </div> )}Anatomy
<InputGroup> <InputGroupAddon> <InputGroupText>tidewater.app/</InputGroupText> </InputGroupAddon> <InputGroupInput defaultValue="northbeam" /></InputGroup>Guidance
Use it when
- A fixed prefix or suffix: a URL host, a currency, a unit.
- A search field with an icon or a keyboard shortcut.
- An inline action that belongs to the value, such as copy or show.
Avoid
- Separate buttons that act on the whole form. Put them next to the group, or use ButtonGroup.
Rules for AI agents
Published in llms-full.txt, the registry and the MCP server, so coding agents follow them when they build with this component.
- Use InputGroupInput, not Input, inside the group. It inherits the group size.
- Put addons in InputGroupAddon with align inline-start or inline-end. Text goes in InputGroupText.
- Icon-only InputGroupButton needs aria-label.
- Set size on InputGroup, not on the input.
API
InputGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "default" | "lg" | "default" | Height of the group and its input. |
InputGroupAddon
| Prop | Type | Default | Description |
|---|---|---|---|
| align | "inline-start" | "inline-end" | "block-start" | "block-end" | "inline-start" | Where the addon sits. |
InputGroupButton
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "xs" | "sm" | "icon-xs" | "icon-sm" | "xs" | Compact sizes that fit inside the group. |
Installation
The Prodular UI CLI adds the source to components/ui with everything it depends on. In a project that already uses shadcn/ui, the shadcn CLI installs it from the Prodular registry too. See Installation for the first setup.
npx prodular-ui add input-group# or, with the registry in components.jsonnpx shadcn@latest add @prodular/input-group