- 手风琴 Accordion
- 警告 Alert
- 警告对话框 Alert Dialog
- 宽高比 Aspect Ratio
- 头像 Avatar
- 徽章 Badge
- 面包屑 Breadcrumb
- 按钮 Button
- 按钮组 Button Group
- 日历 Calendar
- 卡片 Card
- 轮播图 Carousel
- 图表 Chart
- 复选框 Checkbox
- 折叠面板 Collapsible
- 组合框 Combobox
- 命令菜单 Command
- 上下文菜单 Context Menu
- 数据表格 Data Table
- 日期选择器 Date Picker
- 对话框 Dialog
- 抽屉 Drawer
- 下拉菜单 Dropdown Menu
- 空状态 Empty
- 字段 Field
- 表单 Form
- 悬停卡片 Hover Card
- 输入 Input
- 输入组 Input Group
- 一次性密码 OTP
- 条目 Item
- 快捷键 Kbd
- 标签 Label
- 菜单栏 Menubar
- 导航菜单 Navigation Menu
- 分页 Pagination
- 弹出层 Popover
- 进度 Progress
- 单选框组 Radio Group
- 可调整大小 Resizable
- 滚动区域 Scroll Area
- 选择框 Select
- 分隔符 Separator
- 侧边栏 Sheet
- 侧边栏 Sidebar
- 骨架屏 Skeleton
- 滑块 Slider
- 通知 Sonner
- 加载指示器 Spinner
- 开关 Switch
- 表格 Table
- 标签页 Tabs
- 文本区域 Textarea
- 消息 Toast
- 切换按钮 Toggle
- 切换组 Toggle Group
- 提示 Tooltip
- 排版 Typography
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>
示例
图标
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>
)
}
文字
在输入框旁显示附加文本信息。
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>
)
}
按钮
在输入组中添加按钮以执行操作。
"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>
)
}
提示框
添加提示框以提供额外上下文或帮助。
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>
)
}
文本域
输入组同样支持文本域组件。使用 block-start
或 block-end
进行对齐。
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>
)
}
加载旋转器
处理输入时显示加载指示器。
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>
)
}
标签
在输入组内添加标签以提升无障碍性。
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>
)
}
下拉菜单
将输入组与下拉菜单结合,支持复杂交互。
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>
)
}
按钮组
用按钮组包裹输入组以创建前缀和后缀。
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>
)
}
自定义输入
给你的自定义输入添加 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="自动调整高度文本域..."
/>
<InputGroupAddon align="block-end">how</InputGroupAddon>
</InputGroup>
)
}
API 参考
InputGroup
包裹输入和附加内容的主组件。
属性 | 类型 | 默认值 |
---|---|---|
className | string |
<InputGroup>
<InputGroupInput />
<InputGroupAddon />
</InputGroup>
InputGroupAddon
显示图标、文字、按钮或其他内容在输入旁边。
为了正确的焦点导航,InputGroupAddon
组件应放置在输入框之后。通过设置 align
属性来定位附加内容。
属性 | 类型 | 默认值 |
---|---|---|
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>按钮</InputGroupButton>
<InputGroupButton>按钮</InputGroupButton>
</InputGroupAddon>
InputGroupButton
在输入组内显示按钮。
属性 | 类型 | 默认值 |
---|---|---|
size | "xs" | "icon-xs" | "sm" | "icon-sm" | "xs" |
variant | "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "ghost" |
className | string |
<InputGroupButton>按钮</InputGroupButton>
<InputGroupButton size="icon-xs" aria-label="复制">
<CopyIcon />
</InputGroupButton>
InputGroupInput
构建输入组时替代 <Input />
。该组件预先应用了输入组样式,并使用统一的 data-slot="input-group-control"
处理聚焦状态。
属性 | 类型 | 默认值 |
---|---|---|
className | string |
其他所有属性会透传给底层的 <Input />
组件。
<InputGroup>
<InputGroupInput placeholder="请输入内容..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
InputGroupTextarea
构建输入组时替代 <Textarea />
。该组件预先应用了文本域组样式,并使用统一的 data-slot="input-group-control"
处理聚焦状态。
属性 | 类型 | 默认值 |
---|---|---|
className | string |
其他所有属性会透传给底层的 <Textarea />
组件。
<InputGroup>
<InputGroupTextarea placeholder="输入消息..." />
<InputGroupAddon align="block-end">
<InputGroupButton>发送</InputGroupButton>
</InputGroupAddon>
</InputGroup>
更新日志
2025-10-06 InputGroup
给 InputGroup
组件添加了 min-w-0
类。详情参见 diff。