- 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
December 2025 - npx shadcn create
From the very first commit, the goal of shadcn/ui was to make it customizable.
The idea is to give you solid defaults, spacing, color tokens, animations, accessibility, and then let you take it from there. Tweak the code. Add new components. Change the colors. Build your own version.
But somewhere along the way, all apps started looking the same. I guess the defaults were a little too good. My bad.
Today, we're changing that: npx shadcn create.
Customize Everything. Pick your component library, icons, base color, theme, fonts and create your own version of shadcn/ui.
We're starting with 5 new visual styles, designed to help your UI actually feel like your UI.
- Vega – The classic shadcn/ui look.
- Nova – Reduced padding and margins for compact layouts.
- Maia – Soft and rounded, with generous spacing.
- Lyra – Boxy and sharp. Pairs well with mono fonts.
- Mira – Compact. Made for dense interfaces.
This goes beyond theming.
Your config doesn’t just change colors, it rewrites the component code to match your setup. Fonts, spacing, structure, even the libraries you use, everything adapts to your preferences.
The new CLI takes care of it all.
Start with a component library. Choose between Radix or Base UI.
We rebuilt every component for Base UI, keeping the same abstraction. They are fully compatible with your existing components, even those pulled from remote registries.
When you pull down components, we auto-detect your library and apply the right transformations.
It's time to build something that doesn't look like everything else.
Now available for Next.js, Vite, TanStack Start and v0.
Get Started
November 2025 - Registry directory
We just published the Registry Directory: a list of code registries you can browse and pull code and components from.
https://ui.shadcn.com/docs/directory
Built into the CLI. No config required.
October 2025 - New Components
2025 年 10 月:全新组件
这次的组件更新,我回顾了我们日常反复构建的那些“无聊”部分,把它们提炼成真正可复用的抽象。
这些组件可与任意组件库一起使用——Radix、Base UI、React Aria 等等。直接复制粘贴到你的项目即可。
- Spinner:用于展示加载状态的指示器。
- Kbd:展示单个或组合快捷键。
- Button Group:用于操作组或分裂按钮的按钮组容器。
- Input Group:带图标、按钮、标签等的输入容器。
- Field:一个组件搞定所有表单。
- Item:用于展示条目、卡片等列表内容。
- Empty:用于展示空状态的组件。
Spinner
先从最简单的组件开始:Spinner 和 Kbd。很基础,你一定知道它们的用途。
渲染一个 spinner:
import { Spinner } from "@/components/ui/spinner"<Spinner />显示效果如下:
import { Spinner } from "@/components/ui/spinner"
export function SpinnerBasic() {
return (
<div className="flex flex-col items-center justify-center gap-8">
<Spinner />
</div>
)
}
在按钮中使用:
import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"
export function SpinnerButton() {
return (
<div className="flex flex-col items-center gap-4">
<Button disabled size="sm">
<Spinner />
Loading...
</Button>
<Button variant="outline" disabled size="sm">
<Spinner />
Please wait
</Button>
<Button variant="secondary" disabled size="sm">
<Spinner />
Processing
</Button>
</div>
)
}
你也可以编辑代码,替换成自己的自定义 spinner。
import { LoaderIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
return (
<LoaderIcon
role="status"
aria-label="Loading"
className={cn("size-4 animate-spin", className)}
{...props}
/>
)
}
export function SpinnerCustom() {
return (
<div className="flex items-center gap-4">
<Spinner />
</div>
)
}
Kbd
Kbd 组件用于渲染键盘按键。
import { Kbd, KbdGroup } from "@/components/ui/kbd"<Kbd>Ctrl</Kbd>使用 KbdGroup 可以组合多个按键:
<KbdGroup>
<Kbd>Ctrl</Kbd>
<Kbd>B</Kbd>
</KbdGroup>import { Kbd, KbdGroup } from "@/components/ui/kbd"
export function KbdDemo() {
return (
<div className="flex flex-col items-center gap-4">
<KbdGroup>
<Kbd>⌘</Kbd>
<Kbd>⇧</Kbd>
<Kbd>⌥</Kbd>
<Kbd>⌃</Kbd>
</KbdGroup>
<KbdGroup>
<Kbd>Ctrl</Kbd>
<span>+</span>
<Kbd>B</Kbd>
</KbdGroup>
</div>
)
}
你可以把它放进按钮、提示、输入组等各类组件中。
Button Group
常收到的需求之一:按钮组。它是一个容器,用来把相关按钮组合在一起,样式统一,适合操作组、分裂按钮等场景。
"use client"
import * as React from "react"
import {
ArchiveIcon,
ArrowLeftIcon,
CalendarPlusIcon,
ClockIcon,
ListFilterIcon,
MailCheckIcon,
MoreHorizontalIcon,
TagIcon,
Trash2Icon,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function ButtonGroupDemo() {
const [label, setLabel] = React.useState("personal")
return (
<ButtonGroup>
<ButtonGroup className="hidden sm:flex">
<Button variant="outline" size="icon" aria-label="Go Back">
<ArrowLeftIcon />
</Button>
</ButtonGroup>
<ButtonGroup>
<Button variant="outline">Archive</Button>
<Button variant="outline">Report</Button>
</ButtonGroup>
<ButtonGroup>
<Button variant="outline">Snooze</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon" aria-label="More Options">
<MoreHorizontalIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-52">
<DropdownMenuGroup>
<DropdownMenuItem>
<MailCheckIcon />
Mark as Read
</DropdownMenuItem>
<DropdownMenuItem>
<ArchiveIcon />
Archive
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<ClockIcon />
Snooze
</DropdownMenuItem>
<DropdownMenuItem>
<CalendarPlusIcon />
Add to Calendar
</DropdownMenuItem>
<DropdownMenuItem>
<ListFilterIcon />
Add to List
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<TagIcon />
Label As...
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuRadioGroup
value={label}
onValueChange={setLabel}
>
<DropdownMenuRadioItem value="personal">
Personal
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="work">
Work
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="other">
Other
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem variant="destructive">
<Trash2Icon />
Trash
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</ButtonGroup>
</ButtonGroup>
)
}
使用方式:
import { ButtonGroup } from "@/components/ui/button-group"<ButtonGroup>
<Button>Button 1</Button>
<Button>Button 2</Button>
</ButtonGroup>可以嵌套按钮组构建更复杂的布局:
<ButtonGroup>
<ButtonGroup>
<Button>Button 1</Button>
<Button>Button 2</Button>
</ButtonGroup>
<ButtonGroup>
<Button>Button 3</Button>
<Button>Button 4</Button>
</ButtonGroup>
</ButtonGroup>使用 ButtonGroupSeparator 可实现分裂按钮(经典下拉模式)。
"use client"
import {
AlertTriangleIcon,
CheckIcon,
ChevronDownIcon,
CopyIcon,
ShareIcon,
TrashIcon,
UserRoundXIcon,
VolumeOffIcon,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function ButtonGroupDropdown() {
return (
<ButtonGroup>
<Button variant="outline">Follow</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="!pl-2">
<ChevronDownIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="[--radius:1rem]">
<DropdownMenuGroup>
<DropdownMenuItem>
<VolumeOffIcon />
Mute Conversation
</DropdownMenuItem>
<DropdownMenuItem>
<CheckIcon />
Mark as Read
</DropdownMenuItem>
<DropdownMenuItem>
<AlertTriangleIcon />
Report Conversation
</DropdownMenuItem>
<DropdownMenuItem>
<UserRoundXIcon />
Block User
</DropdownMenuItem>
<DropdownMenuItem>
<ShareIcon />
Share Conversation
</DropdownMenuItem>
<DropdownMenuItem>
<CopyIcon />
Copy Conversation
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem variant="destructive">
<TrashIcon />
Delete Conversation
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</ButtonGroup>
)
}
也可以给输入框添加前后缀按钮或文本:
"use client"
import * as React from "react"
import { ArrowRightIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "@/components/ui/select"
const CURRENCIES = [
{
value: "$",
label: "US Dollar",
},
{
value: "€",
label: "Euro",
},
{
value: "£",
label: "British Pound",
},
]
export function ButtonGroupSelect() {
const [currency, setCurrency] = React.useState("$")
return (
<ButtonGroup>
<ButtonGroup>
<Select value={currency} onValueChange={setCurrency}>
<SelectTrigger className="font-mono">{currency}</SelectTrigger>
<SelectContent className="min-w-24">
{CURRENCIES.map((currency) => (
<SelectItem key={currency.value} value={currency.value}>
{currency.value}{" "}
<span className="text-muted-foreground">{currency.label}</span>
</SelectItem>
))}
</SelectContent>
</Select>
<Input placeholder="10.00" pattern="[0-9]*" />
</ButtonGroup>
<ButtonGroup>
<Button aria-label="Send" size="icon" variant="outline">
<ArrowRightIcon />
</Button>
</ButtonGroup>
</ButtonGroup>
)
}
<ButtonGroup>
<ButtonGroupText>Prefix</ButtonGroupText>
<Input placeholder="Type something here..." />
<Button>Button</Button>
</ButtonGroup>Input Group
Input Group 用于给输入框增加图标、按钮等。那些你总是想要放在输入框周围的小功能都可以塞进来。
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group"<InputGroup>
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<SearchIcon />
</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>
)
}
可以加入按钮:
"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 {
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>
)
}
支持 textarea,让你快速构建各种带旋钮、参数的表单或提示输入。
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 等元素组合:
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>
)
}
Field
压轴登场的 Field:用于构建复杂表单的核心组件。这个抽象花了我很长时间,但最终实现了对所有表单库的支持:Server Actions、React Hook Form、TanStack Form,甚至你自己的表单方案。
import {
Field,
FieldDescription,
FieldError,
FieldLabel,
} from "@/components/ui/field"基础用法:
<Field>
<FieldLabel htmlFor="username">Username</FieldLabel>
<Input id="username" placeholder="Max Leiter" />
<FieldDescription>
Choose a unique username for your account.
</FieldDescription>
</Field>import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldSet,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
export function FieldInput() {
return (
<div className="w-full max-w-md">
<FieldSet>
<FieldGroup>
<Field>
<FieldLabel htmlFor="username">Username</FieldLabel>
<Input id="username" type="text" placeholder="Max Leiter" />
<FieldDescription>
Choose a unique username for your account.
</FieldDescription>
</Field>
<Field>
<FieldLabel htmlFor="password">Password</FieldLabel>
<FieldDescription>
Must be at least 8 characters long.
</FieldDescription>
<Input id="password" type="password" placeholder="••••••••" />
</Field>
</FieldGroup>
</FieldSet>
</div>
)
}
它支持所有表单控件:输入框、文本域、选择器、复选框、单选框、开关、滑块……下面是一个完整示例:
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
export function FieldDemo() {
return (
<div className="w-full max-w-md">
<form>
<FieldGroup>
<FieldSet>
<FieldLegend>Payment Method</FieldLegend>
<FieldDescription>
All transactions are secure and encrypted
</FieldDescription>
<FieldGroup>
<Field>
<FieldLabel htmlFor="checkout-7j9-card-name-43j">
Name on Card
</FieldLabel>
<Input
id="checkout-7j9-card-name-43j"
placeholder="Evil Rabbit"
required
/>
</Field>
<Field>
<FieldLabel htmlFor="checkout-7j9-card-number-uw1">
Card Number
</FieldLabel>
<Input
id="checkout-7j9-card-number-uw1"
placeholder="1234 5678 9012 3456"
required
/>
<FieldDescription>
Enter your 16-digit card number
</FieldDescription>
</Field>
<div className="grid grid-cols-3 gap-4">
<Field>
<FieldLabel htmlFor="checkout-exp-month-ts6">
Month
</FieldLabel>
<Select defaultValue="">
<SelectTrigger id="checkout-exp-month-ts6">
<SelectValue placeholder="MM" />
</SelectTrigger>
<SelectContent>
<SelectItem value="01">01</SelectItem>
<SelectItem value="02">02</SelectItem>
<SelectItem value="03">03</SelectItem>
<SelectItem value="04">04</SelectItem>
<SelectItem value="05">05</SelectItem>
<SelectItem value="06">06</SelectItem>
<SelectItem value="07">07</SelectItem>
<SelectItem value="08">08</SelectItem>
<SelectItem value="09">09</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="11">11</SelectItem>
<SelectItem value="12">12</SelectItem>
</SelectContent>
</Select>
</Field>
<Field>
<FieldLabel htmlFor="checkout-7j9-exp-year-f59">
Year
</FieldLabel>
<Select defaultValue="">
<SelectTrigger id="checkout-7j9-exp-year-f59">
<SelectValue placeholder="YYYY" />
</SelectTrigger>
<SelectContent>
<SelectItem value="2024">2024</SelectItem>
<SelectItem value="2025">2025</SelectItem>
<SelectItem value="2026">2026</SelectItem>
<SelectItem value="2027">2027</SelectItem>
<SelectItem value="2028">2028</SelectItem>
<SelectItem value="2029">2029</SelectItem>
</SelectContent>
</Select>
</Field>
<Field>
<FieldLabel htmlFor="checkout-7j9-cvv">CVV</FieldLabel>
<Input id="checkout-7j9-cvv" placeholder="123" required />
</Field>
</div>
</FieldGroup>
</FieldSet>
<FieldSeparator />
<FieldSet>
<FieldLegend>Billing Address</FieldLegend>
<FieldDescription>
The billing address associated with your payment method
</FieldDescription>
<FieldGroup>
<Field orientation="horizontal">
<Checkbox
id="checkout-7j9-same-as-shipping-wgm"
defaultChecked
/>
<FieldLabel
htmlFor="checkout-7j9-same-as-shipping-wgm"
className="font-normal"
>
Same as shipping address
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
<FieldSet>
<FieldGroup>
<Field>
<FieldLabel htmlFor="checkout-7j9-optional-comments">
Comments
</FieldLabel>
<Textarea
id="checkout-7j9-optional-comments"
placeholder="Add any additional comments"
className="resize-none"
/>
</Field>
</FieldGroup>
</FieldSet>
<Field orientation="horizontal">
<Button type="submit">Submit</Button>
<Button variant="outline" type="button">
Cancel
</Button>
</Field>
</FieldGroup>
</form>
</div>
)
}
一些复选框示例:
Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.
import { Checkbox } from "@/components/ui/checkbox"
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
} from "@/components/ui/field"
export function FieldCheckbox() {
return (
<div className="w-full max-w-md">
<FieldGroup>
<FieldSet>
<FieldLegend variant="label">
Show these items on the desktop
</FieldLegend>
<FieldDescription>
Select the items you want to show on the desktop.
</FieldDescription>
<FieldGroup className="gap-3">
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-hard-disks-ljj" />
<FieldLabel
htmlFor="finder-pref-9k2-hard-disks-ljj"
className="font-normal"
defaultChecked
>
Hard disks
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-external-disks-1yg" />
<FieldLabel
htmlFor="finder-pref-9k2-external-disks-1yg"
className="font-normal"
>
External disks
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-cds-dvds-fzt" />
<FieldLabel
htmlFor="finder-pref-9k2-cds-dvds-fzt"
className="font-normal"
>
CDs, DVDs, and iPods
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-connected-servers-6l2" />
<FieldLabel
htmlFor="finder-pref-9k2-connected-servers-6l2"
className="font-normal"
>
Connected servers
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
<FieldSeparator />
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-sync-folders-nep" defaultChecked />
<FieldContent>
<FieldLabel htmlFor="finder-pref-9k2-sync-folders-nep">
Sync Desktop & Documents folders
</FieldLabel>
<FieldDescription>
Your Desktop & Documents folders are being synced with iCloud
Drive. You can access them from other devices.
</FieldDescription>
</FieldContent>
</Field>
</FieldGroup>
</div>
)
}
通过 FieldGroup 与 FieldSet 把字段组合起来,做多段式表单再合适不过。
<FieldSet>
<FieldLegend />
<FieldGroup>
<Field />
<Field />
</FieldGroup>
</FieldSet>import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
export function FieldFieldset() {
return (
<div className="w-full max-w-md space-y-6">
<FieldSet>
<FieldLegend>Address Information</FieldLegend>
<FieldDescription>
We need your address to deliver your order.
</FieldDescription>
<FieldGroup>
<Field>
<FieldLabel htmlFor="street">Street Address</FieldLabel>
<Input id="street" type="text" placeholder="123 Main St" />
</Field>
<div className="grid grid-cols-2 gap-4">
<Field>
<FieldLabel htmlFor="city">City</FieldLabel>
<Input id="city" type="text" placeholder="New York" />
</Field>
<Field>
<FieldLabel htmlFor="zip">Postal Code</FieldLabel>
<Input id="zip" type="text" placeholder="90502" />
</Field>
</div>
</FieldGroup>
</FieldSet>
</div>
)
}
响应式也非常简单。使用 orientation="responsive",组件会根据容器宽度在纵向与横向间自动切换。
import { Button } from "@/components/ui/button"
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
export function FieldResponsive() {
return (
<div className="w-full max-w-4xl">
<form>
<FieldSet>
<FieldLegend>Profile</FieldLegend>
<FieldDescription>Fill in your profile information.</FieldDescription>
<FieldSeparator />
<FieldGroup>
<Field orientation="responsive">
<FieldContent>
<FieldLabel htmlFor="name">Name</FieldLabel>
<FieldDescription>
Provide your full name for identification
</FieldDescription>
</FieldContent>
<Input id="name" placeholder="Evil Rabbit" required />
</Field>
<FieldSeparator />
<Field orientation="responsive">
<FieldContent>
<FieldLabel htmlFor="lastName">Message</FieldLabel>
<FieldDescription>
You can write your message here. Keep it short, preferably
under 100 characters.
</FieldDescription>
</FieldContent>
<Textarea
id="message"
placeholder="Hello, world!"
required
className="min-h-[100px] resize-none sm:min-w-[300px]"
/>
</Field>
<FieldSeparator />
<Field orientation="responsive">
<Button type="submit">Submit</Button>
<Button type="button" variant="outline">
Cancel
</Button>
</Field>
</FieldGroup>
</FieldSet>
</form>
</div>
)
}
还有一个小技巧:用 FieldLabel 包裹字段即可做出可选中区域的组合项,颜值与交互都很好。
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldSet,
FieldTitle,
} from "@/components/ui/field"
import {
RadioGroup,
RadioGroupItem,
} from "@/components/ui/radio-group"
export function FieldChoiceCard() {
return (
<div className="w-full max-w-md">
<FieldGroup>
<FieldSet>
<FieldLabel htmlFor="compute-environment-p8w">
Compute Environment
</FieldLabel>
<FieldDescription>
Select the compute environment for your cluster.
</FieldDescription>
<RadioGroup defaultValue="kubernetes">
<FieldLabel htmlFor="kubernetes-r2h">
<Field orientation="horizontal">
<FieldContent>
<FieldTitle>Kubernetes</FieldTitle>
<FieldDescription>
Run GPU workloads on a K8s configured cluster.
</FieldDescription>
</FieldContent>
<RadioGroupItem value="kubernetes" id="kubernetes-r2h" />
</Field>
</FieldLabel>
<FieldLabel htmlFor="vm-z4k">
<Field orientation="horizontal">
<FieldContent>
<FieldTitle>Virtual Machine</FieldTitle>
<FieldDescription>
Access a VM configured cluster to run GPU workloads.
</FieldDescription>
</FieldContent>
<RadioGroupItem value="vm" id="vm-z4k" />
</Field>
</FieldLabel>
</RadioGroup>
</FieldSet>
</FieldGroup>
</div>
)
}
Item
这是一个通用的 flex 容器,几乎可以容纳任何类型的内容。
我之前已经写过无数次类似结构,于是索性做成组件,现在几乎到处都用它:展示条目列表、卡片等等。
import {
Item,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item"基础示例:
<Item>
<ItemMedia variant="icon">
<HomeIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Dashboard</ItemTitle>
<ItemDescription>Overview of your account and activity.</ItemDescription>
</ItemContent>
</Item>A simple item with title and description.
import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item"
export function ItemDemo() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
<Item variant="outline">
<ItemContent>
<ItemTitle>Basic Item</ItemTitle>
<ItemDescription>
A simple item with title and description.
</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="outline" size="sm">
Action
</Button>
</ItemActions>
</Item>
<Item variant="outline" size="sm" asChild>
<a href="#">
<ItemMedia>
<BadgeCheckIcon className="size-5" />
</ItemMedia>
<ItemContent>
<ItemTitle>Your profile has been verified.</ItemTitle>
</ItemContent>
<ItemActions>
<ChevronRightIcon className="size-4" />
</ItemActions>
</a>
</Item>
</div>
)
}
可以添加图标、头像或图片:
New login detected from unknown device.
import { ShieldAlertIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item"
export function ItemIcon() {
return (
<div className="flex w-full max-w-lg flex-col gap-6">
<Item variant="outline">
<ItemMedia variant="icon">
<ShieldAlertIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Security Alert</ItemTitle>
<ItemDescription>
New login detected from unknown device.
</ItemDescription>
</ItemContent>
<ItemActions>
<Button size="sm" variant="outline">
Review
</Button>
</ItemActions>
</Item>
</div>
)
}
Last seen 5 months ago
Invite your team to collaborate on this project.
import { Plus } from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item"
export function ItemAvatar() {
return (
<div className="flex w-full max-w-lg flex-col gap-6">
<Item variant="outline">
<ItemMedia>
<Avatar className="size-10">
<AvatarImage src="https://github.com/evilrabbit.png" />
<AvatarFallback>ER</AvatarFallback>
</Avatar>
</ItemMedia>
<ItemContent>
<ItemTitle>Evil Rabbit</ItemTitle>
<ItemDescription>Last seen 5 months ago</ItemDescription>
</ItemContent>
<ItemActions>
<Button
size="icon-sm"
variant="outline"
className="rounded-full"
aria-label="Invite"
>
<Plus />
</Button>
</ItemActions>
</Item>
<Item variant="outline">
<ItemMedia>
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
<Avatar className="hidden sm:flex">
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar className="hidden sm:flex">
<AvatarImage
src="https://github.com/maxleiter.png"
alt="@maxleiter"
/>
<AvatarFallback>LR</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
src="https://github.com/evilrabbit.png"
alt="@evilrabbit"
/>
<AvatarFallback>ER</AvatarFallback>
</Avatar>
</div>
</ItemMedia>
<ItemContent>
<ItemTitle>No Team Members</ItemTitle>
<ItemDescription>
Invite your team to collaborate on this project.
</ItemDescription>
</ItemContent>
<ItemActions>
<Button size="sm" variant="outline">
Invite
</Button>
</ItemActions>
</Item>
</div>
)
}
使用 ItemGroup 可以轻松渲染条目列表:
shadcn@vercel.com
maxleiter@vercel.com
evilrabbit@vercel.com
import * as React from "react"
import { PlusIcon } from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemGroup,
ItemMedia,
ItemSeparator,
ItemTitle,
} from "@/components/ui/item"
const people = [
{
username: "shadcn",
avatar: "https://github.com/shadcn.png",
email: "shadcn@vercel.com",
},
{
username: "maxleiter",
avatar: "https://github.com/maxleiter.png",
email: "maxleiter@vercel.com",
},
{
username: "evilrabbit",
avatar: "https://github.com/evilrabbit.png",
email: "evilrabbit@vercel.com",
},
]
export function ItemGroupExample() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
<ItemGroup>
{people.map((person, index) => (
<React.Fragment key={person.username}>
<Item>
<ItemMedia>
<Avatar>
<AvatarImage src={person.avatar} className="grayscale" />
<AvatarFallback>{person.username.charAt(0)}</AvatarFallback>
</Avatar>
</ItemMedia>
<ItemContent className="gap-1">
<ItemTitle>{person.username}</ItemTitle>
<ItemDescription>{person.email}</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="ghost" size="icon" className="rounded-full">
<PlusIcon />
</Button>
</ItemActions>
</Item>
{index !== people.length - 1 && <ItemSeparator />}
</React.Fragment>
))}
</ItemGroup>
</div>
)
}
需要链接?使用 asChild 即可:
<Item asChild>
<a href="/dashboard">
<ItemMedia variant="icon">
<HomeIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Dashboard</ItemTitle>
<ItemDescription>Overview of your account and activity.</ItemDescription>
</ItemContent>
</a>
</Item>import { ChevronRightIcon, ExternalLinkIcon } from "lucide-react"
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemTitle,
} from "@/components/ui/item"
export function ItemLink() {
return (
<div className="flex w-full max-w-md flex-col gap-4">
<Item asChild>
<a href="#">
<ItemContent>
<ItemTitle>Visit our documentation</ItemTitle>
<ItemDescription>
Learn how to get started with our components.
</ItemDescription>
</ItemContent>
<ItemActions>
<ChevronRightIcon className="size-4" />
</ItemActions>
</a>
</Item>
<Item variant="outline" asChild>
<a href="#" target="_blank" rel="noopener noreferrer">
<ItemContent>
<ItemTitle>External resource</ItemTitle>
<ItemDescription>
Opens in a new tab with security attributes.
</ItemDescription>
</ItemContent>
<ItemActions>
<ExternalLinkIcon className="size-4" />
</ItemActions>
</a>
</Item>
</div>
)
}
Empty
最后一个:Empty。用于在应用中展示空状态。
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyMedia,
EmptyTitle,
} from "@/components/ui/empty"使用示例:
<Empty>
<EmptyMedia variant="icon">
<InboxIcon />
</EmptyMedia>
<EmptyTitle>No messages</EmptyTitle>
<EmptyDescription>You don't have any messages yet.</EmptyDescription>
<EmptyContent>
<Button>Send a message</Button>
</EmptyContent>
</Empty>import { IconFolderCode } from "@tabler/icons-react"
import { ArrowUpRightIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
} from "@/components/ui/empty"
export function EmptyDemo() {
return (
<Empty>
<EmptyHeader>
<EmptyMedia variant="icon">
<IconFolderCode />
</EmptyMedia>
<EmptyTitle>No Projects Yet</EmptyTitle>
<EmptyDescription>
You haven't created any projects yet. Get started by creating
your first project.
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<div className="flex gap-2">
<Button>Create Project</Button>
<Button variant="outline">Import Project</Button>
</div>
</EmptyContent>
<Button
variant="link"
asChild
className="text-muted-foreground"
size="sm"
>
<a href="#">
Learn More <ArrowUpRightIcon />
</a>
</Button>
</Empty>
)
}
也可以配合头像使用:
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
} from "@/components/ui/empty"
export function EmptyAvatar() {
return (
<Empty>
<EmptyHeader>
<EmptyMedia variant="default">
<Avatar className="size-12">
<AvatarImage
src="https://github.com/shadcn.png"
className="grayscale"
/>
<AvatarFallback>LR</AvatarFallback>
</Avatar>
</EmptyMedia>
<EmptyTitle>User Offline</EmptyTitle>
<EmptyDescription>
This user is currently offline. You can leave a message to notify them
or try again later.
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button size="sm">Leave Message</Button>
</EmptyContent>
</Empty>
)
}
或与输入组结合,用于搜索结果、邮件订阅等场景:
import { SearchIcon } from "lucide-react"
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyHeader,
EmptyTitle,
} from "@/components/ui/empty"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group"
import { Kbd } from "@/components/ui/kbd"
export function EmptyInputGroup() {
return (
<Empty>
<EmptyHeader>
<EmptyTitle>404 - Not Found</EmptyTitle>
<EmptyDescription>
The page you're looking for doesn't exist. Try searching for
what you need below.
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<InputGroup className="sm:w-3/4">
<InputGroupInput placeholder="Try searching for pages..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<Kbd>/</Kbd>
</InputGroupAddon>
</InputGroup>
<EmptyDescription>
Need help? <a href="#">Contact support</a>
</EmptyDescription>
</EmptyContent>
</Empty>
)
}
以上,七个全新组件,兼容所有库,随时可用于你的项目。
2025 年 9 月:注册表索引
我们创建了一个开源注册表索引,方便直接安装条目。
无需额外配置就能通过 CLI 搜索、查看并添加索引中的组件。所需配置会自动写入你的 components.json。
pnpm dlx shadcn add @ai-elements/prompt-input
完整列表见 https://ui.shadcn.com/r/registries.json。
若要为索引新增注册表,请向 shadcn/ui 仓库提交 PR,详情参见 Registry Index 文档。
2025 年 8 月:shadcn CLI 3.0 与 MCP Server
我们刚发布了 shadcn CLI 3.0,加入命名空间注册表、高级鉴权、新命令以及重写后的注册表引擎。
新增内容
- 命名空间注册表:通过
@registry/name安装组件。 - 私有注册表:使用高级鉴权保护你的注册表。
- 搜索与发现:新命令支持安装前查看代码。
- MCP Server:为所有注册表提供 MCP 服务。
- 全面提速:彻底重写的解析引擎。
- 更好的错误处理:同时照顾用户与 LLM。
- 升级指南:现有用户的迁移说明。
Namespaced Registries
3.0 最大的改动是命名空间注册表。你可以使用 @registry/name 格式,从社区、公司私有或内部注册表安装组件,更方便地跨团队分发代码。
在 components.json 中配置注册表:
{
"registries": {
"@acme": "https://acme.com/r/{name}.json",
"@internal": {
"url": "https://registry.company.com/{name}",
"headers": {
"Authorization": "Bearer ${REGISTRY_TOKEN}"
}
}
}
}然后使用 @registry/name 安装组件:
pnpm dlx shadcn add @acme/button @internal/auth-system
这是完全去中心化的:没有中心注册机构,你可以自定义命名空间并按团队需要组织组件。
{
"registries": {
"@design": "https://registry.company.com/create/{name}.json",
"@engineering": "https://registry.company.com/eng/{name}.json",
"@marketing": "https://registry.company.com/marketing/{name}.json"
}
}组件之间甚至可以互相依赖来自不同注册表的资源,解析与安装都会自动完成。
{
"name": "dashboard",
"type": "registry:block",
"registryDependencies": [
"@shadcn/card",
"@v0/chart",
"@acme/data-table",
"@lib/data-fetcher",
"@ai/analytics-prompt"
]
}Private Registries
需要保密的组件?没问题。可通过令牌、API Key 或自定义请求头配置鉴权:
{
"registries": {
"@private": {
"url": "https://registry.company.com/{name}.json",
"headers": {
"Authorization": "Bearer ${REGISTRY_TOKEN}"
}
}
}
}你的私有组件将只对内部可见,非常适合企业团队。
我们支持常见鉴权方式:Basic Auth、Bearer Token、API Key 查询参数、自定义 Header 等。详情见 鉴权文档。
Search & Discovery
新增三条命令,方便发现所需资源:
- 安装前查看注册表中的条目
pnpm dlx shadcn view @acme/auth-system
- 跨注册表搜索条目
pnpm dlx shadcn search @acme -q auth
- 列出注册表中所有条目
pnpm dlx shadcn list @acme
MCP Server
我们还发布了 shadcn MCP server,支持 MCP 客户端浏览注册表、搜索并安装组件。详情见 MCP 文档。
Faster Everything
注册表解析完全重写,性能更快,依赖解析更稳定。
Improved Error Handling
错误信息更清晰,方便用户与 LLM 理解。
Upgrade Guide
升级提示已整理,请查看 CLI 中的迁移指南。
2025 年 6 月:Calendar 组件
我们将 Calendar 升级至最新版本的 React DayPicker,新增大量特性与优化,并提供 30+ 个日历Blocks可直接使用。详见 Blocks 库。
升级步骤请参阅 Calendar 升级指南。
2025 年 5 月:新站点
我们将 ui.shadcn.com 升级至 Next.js 15.3 与 Tailwind v4,并使用新的 new-york 组件。并进行了一些设计调整,让站点更快、更易用。
这次升级解锁了许多正在开发的新功能,敬请期待。
2025 年 4 月:MCP
我们正在为 shadcn/ui 注册表打造零配置的 MCP 支持。只需一条命令 npx shadcn registry:mcp,即可让任意注册表兼容 MCP。
更多信息见 这条推文。
2025 年 3 月:shadcn 2.5.0
我们本周发布了 shadcn 2.5.0,其中一个很酷的能力:任意位置解析。
注册表现在可以把文件放在应用内的任意位置,我们会正确解析导入。不再受限于固定结构,甚至可以把文件加到注册表之外。安装时我们会跟踪所有文件,进行多轮解析,处理导入与别名——非常快。
2025 年 3 月:跨框架路由支持
shadcn CLI 现在能自动识别你的框架并调整路由,兼容 Laravel、Vite、React Router 等所有框架。
2025 年 2 月:Tailwind v4
我们发布了 Tailwind v4 与 React 19 的首个预览版。你可以立即试用。
新增内容:
- CLI 可初始化 Tailwind v4 项目。
- 完整支持新
@theme指令与@theme inline选项。 - 所有组件已适配 Tailwind v4 与 React 19。
- 移除了 forwardRefs,并调整类型。
- 每个原子组件都有
data-slot属性,方便样式定制。 - 修复并清理了组件样式。
- 弃用
toast组件,推荐使用sonner。 - 按钮改用默认光标。
- 弃用
default风格,新项目默认使用new-york。 - HSL 颜色转换为 OKLCH。
详情见 相关文档。
2025 年 2 月:注册表 Schema 更新
我们正在更新注册表 schema,以支持更多能力。
通过扁平 JSON 文件定义代码,并借助 CLI 分发:
- 自定义样式:引入自己的设计系统、组件与设计令牌
- 扩展、覆盖或混用第三方注册表与 LLM 组件
- 安装主题、CSS 变量、hooks、动画、Tailwind 图层与工具类
2025 年 1 月:Blocks
我们邀请社区为 Blocks 库贡献组件与Blocks,共同打造高质量、可复用的集合,欢迎应用、营销、产品等各类场景。
开始之前请先查看 Blocks 文档。
2024 年 12 月:Monorepo 支持
过去在 monorepo 中使用 shadcn/ui 相当麻烦,虽然可以用 CLI 添加组件,但要自己处理安装位置和导入路径。
现在 CLI 已经理解 monorepo 结构,会自动把组件、依赖与注册表依赖安装到正确位置并处理导入。详情见 Monorepo 文档。
2024 年 11 月:图标
关于图标的更新:new-york 主题现在默认使用 Lucide。
- 新项目默认启用 Lucide
- 现有项目不受影响
- 可通过 CLI(可选)迁移原子组件到 Lucide
更多背景见 相关推文。
2024 年 10 月:React 19
shadcn/ui 现已兼容 React 19 与 Next.js 15,我们也提供了升级指南,帮助你迁移项目。查看 React 19 文档。
2024 年 10 月:Sidebar
发布 sidebar.tsx:包含 25 个侧边栏组件的合集。我不喜欢重复造侧边栏,于是干脆做了 30 多个,再把核心精简成 sidebar.tsx,给你一个可靠的起点。更多内容请关注后续更新。
On This Page
December 2025 - npx shadcn createNovember 2025 - Registry directoryOctober 2025 - New Components2025 年 10 月:全新组件SpinnerKbdButton GroupInput GroupFieldItemEmpty2025 年 9 月:注册表索引2025 年 8 月:shadcn CLI 3.0 与 MCP Server新增内容Namespaced RegistriesPrivate RegistriesSearch & DiscoveryMCP ServerFaster EverythingImproved Error HandlingUpgrade Guide2025 年 6 月:Calendar 组件2025 年 5 月:新站点2025 年 4 月:MCP2025 年 3 月:shadcn 2.5.02025 年 3 月:跨框架路由支持2025 年 2 月:Tailwind v42025 年 2 月:注册表 Schema 更新2025 年 1 月:Blocks2024 年 12 月:Monorepo 支持2024 年 11 月:图标2024 年 10 月:React 192024 年 10 月:Sidebar