Installation
Complete project setup first. Your configuration
must select react/radix and include your Design System ID.
pnpm exec frontfriend download input-group -f react --primitive radixReview dependencies reported by the downloader and keep your source changes in version control.
Usage
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";
<InputGroup className="w-80">
<InputGroupInput
placeholder="Search..."
disabled={false}
/>
<InputGroupAddon align="inline-start">
Search
</InputGroupAddon>
</InputGroup>; Examples
Demo
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<InputGroup className="w-[320px] max-w-xs">
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<DsIcon name="Search" size="4" />
</InputGroupAddon>
<InputGroupAddon align="inline-end">12 results</InputGroupAddon>
</InputGroup>; Inline Start
import { Field, FieldLabel, FieldDescription } from "@/components/ui/field";
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<Field className="w-[360px] max-w-sm">
<FieldLabel htmlFor="inline-start-input">Input</FieldLabel>
<InputGroup>
<InputGroupInput id="inline-start-input" placeholder="Search..." />
<InputGroupAddon align="inline-start">
<DsIcon name="Search" size="4" />
</InputGroupAddon>
</InputGroup>
<FieldDescription>Icon positioned at the start.</FieldDescription>
</Field>; Inline End
import { Field, FieldLabel, FieldDescription } from "@/components/ui/field";
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<Field className="w-[360px] max-w-sm">
<FieldLabel htmlFor="inline-end-input">Input</FieldLabel>
<InputGroup>
<InputGroupInput id="inline-end-input" placeholder="Enter password" />
<InputGroupAddon align="inline-end">
<DsIcon name="EyeOff" size="4" />
</InputGroupAddon>
</InputGroup>
<FieldDescription>Icon positioned at the end.</FieldDescription>
</Field>; Block Start
import {
FieldGroup,
Field,
FieldLabel,
FieldDescription,
} from "@/components/ui/field";
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
InputGroupText,
InputGroupTextarea,
InputGroupButton,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<FieldGroup className="w-[360px] max-w-sm">
<Field className="w-full">
<FieldLabel htmlFor="block-start-input">Input</FieldLabel>
<InputGroup className="h-auto">
<InputGroupInput id="block-start-input" placeholder="Enter your name" />
<InputGroupAddon align="block-start">
<InputGroupText>Full Name</InputGroupText>
</InputGroupAddon>
</InputGroup>
<FieldDescription>Header positioned above the input.</FieldDescription>
</Field>
<Field className="w-full">
<FieldLabel htmlFor="block-start-textarea">Textarea</FieldLabel>
<InputGroup>
<InputGroupTextarea
id="block-start-textarea"
placeholder="console.log('Hello, world!');"
className="font-mono text-sm"
/>
<InputGroupAddon align="block-start" className="border-b">
<DsIcon name="FileCode" size="4" />
<InputGroupText className="font-mono">script.js</InputGroupText>
<InputGroupButton size="icon-xs" className="ml-auto">
<DsIcon name="Copy" size="4" />
<span className="sr-only">Copy</span>
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<FieldDescription>Header positioned above the textarea.</FieldDescription>
</Field>
</FieldGroup>; Block End
import {
FieldGroup,
Field,
FieldLabel,
FieldDescription,
} from "@/components/ui/field";
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
InputGroupText,
InputGroupTextarea,
InputGroupButton,
} from "@/components/ui/input-group";
<FieldGroup className="w-[360px] max-w-sm">
<Field className="w-full">
<FieldLabel htmlFor="block-end-input">Input</FieldLabel>
<InputGroup className="h-auto">
<InputGroupInput id="block-end-input" placeholder="Enter amount" />
<InputGroupAddon align="block-end">
<InputGroupText>USD</InputGroupText>
</InputGroupAddon>
</InputGroup>
<FieldDescription>Footer positioned below the input.</FieldDescription>
</Field>
<Field className="w-full">
<FieldLabel htmlFor="block-end-textarea">Textarea</FieldLabel>
<InputGroup>
<InputGroupTextarea
id="block-end-textarea"
placeholder="Write a comment..."
/>
<InputGroupAddon align="block-end" className="border-t">
<InputGroupText>0/280</InputGroupText>
<InputGroupButton variant="default" size="sm" className="ml-auto">
Post
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<FieldDescription>Footer positioned below the textarea.</FieldDescription>
</Field>
</FieldGroup>; Icon
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-6">
<InputGroup>
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<DsIcon name="Search" size="4" />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput type="email" placeholder="Enter your email" />
<InputGroupAddon>
<DsIcon name="Mail" size="4" />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Card number" />
<InputGroupAddon>
<DsIcon name="CreditCard" size="4" />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<DsIcon name="Check" size="4" />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Card number" />
<InputGroupAddon align="inline-end">
<DsIcon name="Star" size="4" />
<DsIcon name="Search" size="4" />
</InputGroupAddon>
</InputGroup>
</div>; Text
import {
InputGroup,
InputGroupAddon,
InputGroupText,
InputGroupInput,
InputGroupTextarea,
} from "@/components/ui/input-group";
<div className="grid w-[360px] max-w-sm gap-6">
<InputGroup>
<InputGroupAddon>
<InputGroupText>$</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="0.00" />
<InputGroupAddon align="inline-end">
<InputGroupText>USD</InputGroupText>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="example.com" />
<InputGroupAddon align="inline-end">
<InputGroupText>.com</InputGroupText>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Enter your username" />
<InputGroupAddon align="inline-end">
<InputGroupText>@company.com</InputGroupText>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupTextarea placeholder="Enter your message" />
<InputGroupAddon align="block-end">
<InputGroupText className="text-xs">120 characters left</InputGroupText>
</InputGroupAddon>
</InputGroup>
</div>; Button
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
InputGroupButton,
InputGroupText,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-6">
<InputGroup>
<InputGroupInput placeholder="https://x.com/shadcn" readOnly />
<InputGroupAddon align="inline-end">
<InputGroupButton aria-label="Copy" title="Copy" size="icon-xs">
<DsIcon name="Copy" size="4" />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="frontfriend.dev" />
<InputGroupAddon align="inline-end">
<InputGroupButton size="icon-xs">
<DsIcon name="Star" size="4" />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Type to search..." />
<InputGroupAddon align="inline-end">
<InputGroupButton variant="secondary">Search</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</div>; Kbd Example
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
import { Kbd } from "@/components/ui/kbd";
<InputGroup className="w-[360px] max-w-sm">
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<DsIcon name="Search" size="4" />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<Kbd>⌘K</Kbd>
</InputGroupAddon>
</InputGroup>; Dropdown
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
InputGroupButton,
} from "@/components/ui/input-group";
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-4">
<InputGroup>
<InputGroupInput placeholder="Enter file name" />
<InputGroupAddon align="inline-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<InputGroupButton variant="ghost" aria-label="More" size="icon-xs">
<DsIcon name="Ellipsis" size="4" />
</InputGroupButton>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuGroup>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Copy path</DropdownMenuItem>
<DropdownMenuItem>Open location</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Enter search query" />
<InputGroupAddon align="inline-end">
<InputGroupButton className="pr-1.5 text-xs">
Search In... <DsIcon name="ChevronDown" size="3" />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</div>; Spinner Example
import {
InputGroup,
InputGroupInput,
InputGroupAddon,
InputGroupText,
} from "@/components/ui/input-group";
import { Spinner } from "@/components/ui/spinner";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[360px] max-w-sm gap-4">
<InputGroup>
<InputGroupInput placeholder="Searching..." />
<InputGroupAddon align="inline-end">
<Spinner />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Processing..." />
<InputGroupAddon>
<Spinner />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Saving changes..." />
<InputGroupAddon align="inline-end">
<InputGroupText>Saving...</InputGroupText>
<Spinner />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Refreshing data..." />
<InputGroupAddon>
<DsIcon name="Loader" size="4" className="animate-spin" />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InputGroupText>Please wait...</InputGroupText>
</InputGroupAddon>
</InputGroup>
</div>; Textarea
import {
InputGroup,
InputGroupTextarea,
InputGroupAddon,
InputGroupText,
InputGroupButton,
} from "@/components/ui/input-group";
import { Icon as DsIcon } from "@/components/ui/icon";
<div className="grid w-[448px] max-w-md gap-4">
<InputGroup>
<InputGroupTextarea
placeholder="console.log('Hello, world!');"
className="min-h-[200px]"
/>
<InputGroupAddon align="block-end" className="border-t">
<InputGroupText>Line 1, Column 1</InputGroupText>
<InputGroupButton size="sm" className="ml-auto" variant="default">
Run
</InputGroupButton>
</InputGroupAddon>
<InputGroupAddon align="block-start" className="border-b">
<InputGroupText className="font-mono font-medium">
<DsIcon name="FileCode" size="4" /> script.js
</InputGroupText>
<InputGroupButton className="ml-auto" size="icon-xs">
<DsIcon name="RefreshCw" size="4" />
</InputGroupButton>
<InputGroupButton variant="ghost" size="icon-xs">
<DsIcon name="Copy" size="4" />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</div>; Custom Input
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
} from "@/components/ui/input-group";
<div className="grid w-[360px] max-w-sm gap-6">
<InputGroup>
<textarea
data-slot="input-group-control"
className="flex field-sizing-content min-h-16 w-full resize-none rounded-md bg-transparent px-3 py-2.5 text-base transition-[color,box-shadow] outline-none md:text-sm"
placeholder="Autoresize textarea..."
/>
<InputGroupAddon align="block-end">
<InputGroupButton className="ml-auto" size="sm" variant="default">
Submit
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</div>; API reference
Frontfriend-owned props. Native HTML and inherited primitive props remain documented by Radix UI.
InputGroupAddon
| Prop | Type | Default | Description |
|---|---|---|---|
align | InputGroupAlign | "inline-start" |
InputGroupButton
| Prop | Type | Default | Description |
|---|---|---|---|
size | InputGroupButtonSize | "xs" |
Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.