Get Started
组件
- Accordion
- Alert Dialog
- Alert
- Aspect Ratio
- Avatar
- Badge
- Breadcrumb
- Button Group
- Button
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Form
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Label
- Menubar
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle Group
- Toggle
- Tooltip
- Typography
12 results
https://
52% used
import { IconCheck, IconInfoCircle, IconPlus } from "@tabler/icons-react"
import { ArrowUpIcon, Search } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group"
import { Separator } from "@/components/ui/separator"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
export function InputGroupDemo() {
return (
<div className="grid w-full max-w-sm gap-6">
<InputGroup>
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<Search />
</InputGroupAddon>
<InputGroupAddon align="inline-end">12 results</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="example.com" className="!pl-1" />
<InputGroupAddon>
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<Tooltip>
<TooltipTrigger asChild>
<InputGroupButton className="rounded-full" size="icon-xs">
<IconInfoCircle />
</InputGroupButton>
</TooltipTrigger>
<TooltipContent>This is content in a tooltip.</TooltipContent>
</Tooltip>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupTextarea placeholder="Ask, Search or Chat..." />
<InputGroupAddon align="block-end">
<InputGroupButton
variant="outline"
className="rounded-full"
size="icon-xs"
>
<IconPlus />
</InputGroupButton>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<InputGroupButton variant="ghost">Auto</InputGroupButton>
</DropdownMenuTrigger>
<DropdownMenuContent
side="top"
align="start"
className="[--radius:0.95rem]"
>
<DropdownMenuItem>Auto</DropdownMenuItem>
<DropdownMenuItem>Agent</DropdownMenuItem>
<DropdownMenuItem>Manual</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<InputGroupText className="ml-auto">52% used</InputGroupText>
<Separator orientation="vertical" className="!h-4" />
<InputGroupButton
variant="default"
className="rounded-full"
size="icon-xs"
disabled
>
<ArrowUpIcon />
<span className="sr-only">Send</span>
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="@shadcn" />
<InputGroupAddon align="inline-end">
<div className="bg-primary text-primary-foreground flex size-4 items-center justify-center rounded-full">
<IconCheck className="size-3" />
</div>
</InputGroupAddon>
</InputGroup>
</div>
)
}
安装
pnpm dlx shadcn@latest add input-group
用法
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group"<InputGroup>
<InputGroupInput placeholder="搜索..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InputGroupButton>搜索</InputGroupButton>
</InputGroupAddon>
</InputGroup>示例
Icon
import {
CheckIcon,
CreditCardIcon,
InfoIcon,
MailIcon,
SearchIcon,
StarIcon,
} from "lucide-react"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group"
export function InputGroupIcon() {
return (
<div className="grid w-full max-w-sm gap-6">
<InputGroup>
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput type="email" placeholder="Enter your email" />
<InputGroupAddon>
<MailIcon />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Card number" />
<InputGroupAddon>
<CreditCardIcon />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<CheckIcon />
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Card number" />
<InputGroupAddon align="inline-end">
<StarIcon />
<InfoIcon />
</InputGroupAddon>
</InputGroup>
</div>
)
}
Text
在输入框旁展示额外文本信息。
$
USD
https://
.com
@company.com
120 characters left
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group"
export function InputGroupTextExample() {
return (
<div className="grid w-full 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" className="!pl-0.5" />
<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-muted-foreground text-xs">
120 characters left
</InputGroupText>
</InputGroupAddon>
</InputGroup>
</div>
)
}
Button
在输入组中加入按钮以触发操作。
https://
"use client"
import * as React from "react"
import {
IconCheck,
IconCopy,
IconInfoCircle,
IconStar,
} from "@tabler/icons-react"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export function InputGroupButtonExample() {
const { copyToClipboard, isCopied } = useCopyToClipboard()
const [isFavorite, setIsFavorite] = React.useState(false)
return (
<div className="grid w-full 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"
onClick={() => {
copyToClipboard("https://x.com/shadcn")
}}
>
{isCopied ? <IconCheck /> : <IconCopy />}
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<InputGroup className="[--radius:9999px]">
<Popover>
<PopoverTrigger asChild>
<InputGroupAddon>
<InputGroupButton variant="secondary" size="icon-xs">
<IconInfoCircle />
</InputGroupButton>
</InputGroupAddon>
</PopoverTrigger>
<PopoverContent
align="start"
className="flex flex-col gap-1 rounded-xl text-sm"
>
<p className="font-medium">Your connection is not secure.</p>
<p>You should not enter any sensitive information on this site.</p>
</PopoverContent>
</Popover>
<InputGroupAddon className="text-muted-foreground pl-1.5">
https://
</InputGroupAddon>
<InputGroupInput id="input-secure-19" />
<InputGroupAddon align="inline-end">
<InputGroupButton
onClick={() => setIsFavorite(!isFavorite)}
size="icon-xs"
>
<IconStar
data-favorite={isFavorite}
className="data-[favorite=true]:fill-blue-600 data-[favorite=true]:stroke-blue-600"
/>
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Type to search..." />
<InputGroupAddon align="inline-end">
<InputGroupButton variant="secondary">Search</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</div>
)
}
Tooltip
添加提示信息以提供额外说明。
import { HelpCircle, InfoIcon } from "lucide-react"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
export function InputGroupTooltip() {
return (
<div className="grid w-full max-w-sm gap-4">
<InputGroup>
<InputGroupInput placeholder="Enter password" type="password" />
<InputGroupAddon align="inline-end">
<Tooltip>
<TooltipTrigger asChild>
<InputGroupButton
variant="ghost"
aria-label="Info"
size="icon-xs"
>
<InfoIcon />
</InputGroupButton>
</TooltipTrigger>
<TooltipContent>
<p>Password must be at least 8 characters</p>
</TooltipContent>
</Tooltip>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Your email address" />
<InputGroupAddon align="inline-end">
<Tooltip>
<TooltipTrigger asChild>
<InputGroupButton
variant="ghost"
aria-label="Help"
size="icon-xs"
>
<HelpCircle />
</InputGroupButton>
</TooltipTrigger>
<TooltipContent>
<p>We'll use this to send you notifications</p>
</TooltipContent>
</Tooltip>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput placeholder="Enter API key" />
<Tooltip>
<TooltipTrigger asChild>
<InputGroupAddon>
<InputGroupButton
variant="ghost"
aria-label="Help"
size="icon-xs"
>
<HelpCircle />
</InputGroupButton>
</InputGroupAddon>
</TooltipTrigger>
<TooltipContent side="left">
<p>Click for help with API keys</p>
</TooltipContent>
</Tooltip>
</InputGroup>
</div>
)
}
Textarea
输入组同样适用于文本域,可通过 block-start 或 block-end 控制对齐。
Line 1, Column 1
script.js
import {
IconBrandJavascript,
IconCopy,
IconCornerDownLeft,
IconRefresh,
} from "@tabler/icons-react"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group"
export function InputGroupTextareaExample() {
return (
<div className="grid w-full max-w-md gap-4">
<InputGroup>
<InputGroupTextarea
id="textarea-code-32"
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 <IconCornerDownLeft />
</InputGroupButton>
</InputGroupAddon>
<InputGroupAddon align="block-start" className="border-b">
<InputGroupText className="font-mono font-medium">
<IconBrandJavascript />
script.js
</InputGroupText>
<InputGroupButton className="ml-auto" size="icon-xs">
<IconRefresh />
</InputGroupButton>
<InputGroupButton variant="ghost" size="icon-xs">
<IconCopy />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</div>
)
}
Spinner
在处理输入时展示加载状态。
Saving...
Please wait...
import { LoaderIcon } from "lucide-react"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
} from "@/components/ui/input-group"
import { Spinner } from "@/components/ui/spinner"
export function InputGroupSpinner() {
return (
<div className="grid w-full max-w-sm gap-4">
<InputGroup data-disabled>
<InputGroupInput placeholder="Searching..." disabled />
<InputGroupAddon align="inline-end">
<Spinner />
</InputGroupAddon>
</InputGroup>
<InputGroup data-disabled>
<InputGroupInput placeholder="Processing..." disabled />
<InputGroupAddon>
<Spinner />
</InputGroupAddon>
</InputGroup>
<InputGroup data-disabled>
<InputGroupInput placeholder="Saving changes..." disabled />
<InputGroupAddon align="inline-end">
<InputGroupText>Saving...</InputGroupText>
<Spinner />
</InputGroupAddon>
</InputGroup>
<InputGroup data-disabled>
<InputGroupInput placeholder="Refreshing data..." disabled />
<InputGroupAddon>
<LoaderIcon className="animate-spin" />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InputGroupText className="text-muted-foreground">
Please wait...
</InputGroupText>
</InputGroupAddon>
</InputGroup>
</div>
)
}
Label
在输入组中添加标签提升可访问性。
import { InfoIcon } from "lucide-react"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group"
import { Label } from "@/components/ui/label"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
export function InputGroupLabel() {
return (
<div className="grid w-full max-w-sm gap-4">
<InputGroup>
<InputGroupInput id="email" placeholder="shadcn" />
<InputGroupAddon>
<Label htmlFor="email">@</Label>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupInput id="email-2" placeholder="shadcn@vercel.com" />
<InputGroupAddon align="block-start">
<Label htmlFor="email-2" className="text-foreground">
Email
</Label>
<Tooltip>
<TooltipTrigger asChild>
<InputGroupButton
variant="ghost"
aria-label="Help"
className="ml-auto rounded-full"
size="icon-xs"
>
<InfoIcon />
</InputGroupButton>
</TooltipTrigger>
<TooltipContent>
<p>We'll use this to send you notifications</p>
</TooltipContent>
</Tooltip>
</InputGroupAddon>
</InputGroup>
</div>
)
}
Dropdown
与下拉菜单组合实现更复杂的交互。
import { ChevronDownIcon, MoreHorizontal } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group"
export function InputGroupDropdown() {
return (
<div className="grid w-full 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"
>
<MoreHorizontal />
</InputGroupButton>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Copy path</DropdownMenuItem>
<DropdownMenuItem>Open location</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</InputGroupAddon>
</InputGroup>
<InputGroup className="[--radius:1rem]">
<InputGroupInput placeholder="Enter search query" />
<InputGroupAddon align="inline-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<InputGroupButton variant="ghost" className="!pr-1.5 text-xs">
Search In... <ChevronDownIcon className="size-3" />
</InputGroupButton>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="[--radius:0.95rem]">
<DropdownMenuItem>Documentation</DropdownMenuItem>
<DropdownMenuItem>Blog Posts</DropdownMenuItem>
<DropdownMenuItem>Changelog</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</InputGroupAddon>
</InputGroup>
</div>
)
}
Button Group
结合按钮组创建前缀或后缀。
.com
import { Link2Icon } from "lucide-react"
import {
ButtonGroup,
ButtonGroupText,
} from "@/components/ui/button-group"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group"
import { Label } from "@/components/ui/label"
export function InputGroupButtonGroup() {
return (
<div className="grid w-full max-w-sm gap-6">
<ButtonGroup>
<ButtonGroupText asChild>
<Label htmlFor="url">https://</Label>
</ButtonGroupText>
<InputGroup>
<InputGroupInput id="url" />
<InputGroupAddon align="inline-end">
<Link2Icon />
</InputGroupAddon>
</InputGroup>
<ButtonGroupText>.com</ButtonGroupText>
</ButtonGroup>
</div>
)
}
Custom Input
为自定义输入添加 data-slot="input-group-control" 属性,即可自动获得行为与聚焦状态处理。
组件不会对自定义输入应用样式,可通过 className 自行定制。
"use client"
import TextareaAutosize from "react-textarea-autosize"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
} from "@/components/ui/input-group"
export function InputGroupCustom() {
return (
<div className="grid w-full max-w-sm gap-6">
<InputGroup>
<TextareaAutosize
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>
)
}
import { InputGroup, InputGroupAddon } from "@/component/ui/input-group"
import TextareaAutosize from "react-textarea-autosize"
export function InputGroupCustom() {
return (
<InputGroup>
<TextareaAutosize
data-slot="input-group-control"
className="dark:bg-input/30 flex field-sizing-content min-h-16 w-full resize-none rounded-md bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none"
placeholder="Autoresize textarea..."
/>
<InputGroupAddon align="block-end">how</InputGroupAddon>
</InputGroup>
)
}API 参考
InputGroup
负责包裹输入与附加内容的主组件。
| Prop | Type | Default |
|---|---|---|
className | string |
<InputGroup>
<InputGroupInput />
<InputGroupAddon />
</InputGroup>InputGroupAddon
用于在输入两侧展示图标、文本、按钮等内容。
Focus Navigation
为确保焦点顺序正确,应将 InputGroupAddon 放在输入控件之后,并通过 align 属性调整位置。
| Prop | Type | Default |
|---|---|---|
align | "inline-start" | "inline-end" | "block-start" | "block-end" | "inline-start" |
className | string |
<InputGroupAddon align="inline-end">
<SearchIcon />
</InputGroupAddon>在 <InputGroupInput /> 中使用 inline-start 或 inline-end,在 <InputGroupTextarea /> 中使用 block-start 或 block-end。
InputGroupAddon 内可以包含多个 InputGroupButton 以及图标。
<InputGroupAddon>
<InputGroupButton>Button</InputGroupButton>
<InputGroupButton>Button</InputGroupButton>
</InputGroupAddon>InputGroupButton
在输入组中展示按钮。
| Prop | Type | Default |
|---|---|---|
size | "xs" | "icon-xs" | "sm" | "icon-sm" | "xs" |
variant | "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "ghost" |
className | string |
<InputGroupButton>Button</InputGroupButton>
<InputGroupButton size="icon-xs" aria-label="Copy">
<CopyIcon />
</InputGroupButton>InputGroupInput
在输入组场景下替代 <Input />,预置了输入组样式,并通过统一的 data-slot="input-group-control" 处理焦点状态。
| Prop | Type | Default |
|---|---|---|
className | string |
其余属性会透传给底层 <Input /> 组件。
<InputGroup>
<InputGroupInput placeholder="Enter text..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>InputGroupTextarea
输入组场景下替代 <Textarea />,预置文本域样式,并使用统一的 data-slot="input-group-control" 处理焦点状态。
| Prop | Type | Default |
|---|---|---|
className | string |
其余属性会透传给底层 <Textarea /> 组件。
<InputGroup>
<InputGroupTextarea placeholder="Enter message..." />
<InputGroupAddon align="block-end">
<InputGroupButton>Send</InputGroupButton>
</InputGroupAddon>
</InputGroup>更新日志
2025-10-06 InputGroup
为 InputGroup 组件新增 min-w-0 类。详见 diff。