shadcn-ui — for Claude Code shadcn-ui, WEB_PUNTO_TORTUGA, community, for Claude Code, ide skills, ### Add Your First Component, ### Basic Usage, npm install component-library, Component, Library

v1.0.0

关于此技能

适用场景: Ideal for AI agents that need shadcn/ui - component library. 本地化技能摘要: Unlike traditional component libraries, shadcn/ui components are copied directly into your project , giving you full ownership and customization control. This AI agent skill supports Claude Code, Cursor, and Windsurf workflows.

功能特性

shadcn/ui - Component Library
progressive disclosure:
entry point: summary, when to use, quick start
Core Philosophy : Copy-paste components, not npm packages. You own the code.
Use shadcn/ui when :

# 核心主题

NicoReus21 NicoReus21
[0]
[0]
更新于: 4/1/2026

Killer-Skills Review

Decision support comes first. Repository text comes second.

Reference-Only Page Review Score: 10/11

This page remains useful for teams, but Killer-Skills treats it as reference material instead of a primary organic landing page.

Original recommendation layer Concrete use-case guidance Explicit limitations and caution Quality floor passed for review
Review Score
10/11
Quality Score
70
Canonical Locale
en
Detected Body Locale
en

适用场景: Ideal for AI agents that need shadcn/ui - component library. 本地化技能摘要: Unlike traditional component libraries, shadcn/ui components are copied directly into your project , giving you full ownership and customization control. This AI agent skill supports Claude Code, Cursor, and Windsurf workflows.

核心价值

推荐说明: shadcn-ui helps agents shadcn/ui - component library. Unlike traditional component libraries, shadcn/ui components are copied directly into your project , giving you full ownership and customization control. This

适用 Agent 类型

适用场景: Ideal for AI agents that need shadcn/ui - component library.

赋予的主要能力 · shadcn-ui

适用任务: Applying shadcn/ui - Component Library
适用任务: Applying progressive disclosure:
适用任务: Applying entry point: summary, when to use, quick start

! 使用限制与门槛

  • 限制说明: Need accessible, production-ready UI components
  • 限制说明: Need dark mode support out of the box
  • 限制说明: Need legacy browser support (uses modern CSS features)

Why this page is reference-only

  • - Current locale does not satisfy the locale-governance contract.

Source Boundary

The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.

评审后的下一步

先决定动作,再继续看上游仓库材料

Killer-Skills 的主价值不应该停在“帮你打开仓库说明”,而是先帮你判断这项技能是否值得安装、是否应该回到可信集合复核,以及是否已经进入工作流落地阶段。

实验室 Demo

Browser Sandbox Environment

⚡️ Ready to unleash?

Experience this Agent in a zero-setup browser environment powered by WebContainers. No installation required.

Boot Container Sandbox

常见问题与安装步骤

以下问题与步骤与页面结构化数据保持一致,便于搜索引擎理解页面内容。

? FAQ

shadcn-ui 是什么?

适用场景: Ideal for AI agents that need shadcn/ui - component library. 本地化技能摘要: Unlike traditional component libraries, shadcn/ui components are copied directly into your project , giving you full ownership and customization control. This AI agent skill supports Claude Code, Cursor, and Windsurf workflows.

如何安装 shadcn-ui?

运行命令:npx killer-skills add NicoReus21/WEB_PUNTO_TORTUGA/shadcn-ui。支持 Cursor、Windsurf、VS Code、Claude Code 等 19+ IDE/Agent。

shadcn-ui 适用于哪些场景?

典型场景包括:适用任务: Applying shadcn/ui - Component Library、适用任务: Applying progressive disclosure:、适用任务: Applying entry point: summary, when to use, quick start。

shadcn-ui 支持哪些 IDE 或 Agent?

该技能兼容 Cursor, Windsurf, VS Code, Trae, Claude Code, OpenClaw, Aider, Codex, OpenCode, Goose, Cline, Roo Code, Kiro, Augment Code, Continue, GitHub Copilot, Sourcegraph Cody, and Amazon Q Developer。可使用 Killer-Skills CLI 一条命令通用安装。

shadcn-ui 有哪些限制?

限制说明: Need accessible, production-ready UI components;限制说明: Need dark mode support out of the box;限制说明: Need legacy browser support (uses modern CSS features)。

安装步骤

  1. 1. 打开终端

    在你的项目目录中打开终端或命令行。

  2. 2. 执行安装命令

    运行:npx killer-skills add NicoReus21/WEB_PUNTO_TORTUGA/shadcn-ui。CLI 会自动识别 IDE 或 AI Agent 并完成配置。

  3. 3. 开始使用技能

    shadcn-ui 已启用,可立即在当前项目中调用。

! 参考页模式

此页面仍可作为安装与查阅参考,但 Killer-Skills 不再把它视为主要可索引落地页。请优先阅读上方评审结论,再决定是否继续查看上游仓库说明。

Upstream Repository Material

The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.

Upstream Source

shadcn-ui

安装 shadcn-ui,这是一款面向AI agent workflows and automation的 AI Agent Skill。查看评审结论、使用场景与安装路径。

SKILL.md
Readonly
Upstream Repository Material
The section below is imported from the upstream repository and should be treated as secondary evidence. Use the Killer-Skills review above as the primary layer for fit, risk, and installation decisions.
Supporting Evidence

shadcn/ui - Component Library


progressive_disclosure: entry_point: summary, when_to_use, quick_start estimated_tokens: entry: 85 full: 4800

Summary

shadcn/ui is a collection of re-usable React components built with Radix UI primitives and styled with Tailwind CSS. Unlike traditional component libraries, shadcn/ui components are copied directly into your project, giving you full ownership and customization control. Components are accessible, customizable, and open source.

Core Philosophy: Copy-paste components, not npm packages. You own the code.

When to Use

Use shadcn/ui when:

  • Building React applications with Tailwind CSS
  • Need accessible, production-ready UI components
  • Want full control over component code and styling
  • Prefer composition over configuration
  • Building with Next.js, Vite, Remix, or Astro
  • Need dark mode support out of the box
  • Want TypeScript-first components

Don't use when:

  • Not using Tailwind CSS (core styling dependency)
  • Need legacy browser support (uses modern CSS features)
  • Prefer packaged npm libraries over code ownership
  • Building non-React frameworks (Vue, Svelte, Angular)

Quick Start

Installation

bash
1# Initialize shadcn/ui in your project 2npx shadcn-ui@latest init 3 4# Follow interactive prompts: 5# - TypeScript? (yes/no) 6# - Style: Default/New York 7# - Base color: Slate/Gray/Zinc/Neutral/Stone 8# - CSS variables: (yes/no) 9# - React Server Components: (yes/no) 10# - components.json location 11# - Tailwind config location 12# - CSS file location 13# - Import alias (@/components)

Add Your First Component

bash
1# Add individual components 2npx shadcn-ui@latest add button 3npx shadcn-ui@latest add card 4npx shadcn-ui@latest add dialog 5 6# Add multiple components at once 7npx shadcn-ui@latest add button card dialog form input

Basic Usage

tsx
1import { Button } from "@/components/ui/button" 2import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card" 3 4export default function Example() { 5 return ( 6 <Card> 7 <CardHeader> 8 <CardTitle>Welcome</CardTitle> 9 </CardHeader> 10 <CardContent> 11 <Button>Click me</Button> 12 </CardContent> 13 </Card> 14 ) 15}

Architecture

Copy-Paste Philosophy

Key Difference from Traditional Libraries:

  • Traditional: npm install component-library → locked to package versions
  • shadcn/ui: Components copied to components/ui/ → you own the code

Benefits:

  • Full customization control
  • No breaking changes from package updates
  • Easy to modify for specific needs
  • Transparent implementation
  • Tree-shakeable by default

Component Structure

src/
├── components/
│   └── ui/
│       ├── button.tsx      # Component implementation
│       ├── card.tsx        # Owns its code
│       ├── dialog.tsx      # Modifiable
│       └── ...
├── lib/
│   └── utils.ts            # cn() helper for class merging
└── app/
    └── globals.css         # Tailwind directives + CSS variables

Technology Stack

Core Dependencies:

  • Radix UI: Accessible component primitives (headless UI)
  • Tailwind CSS: Utility-first styling
  • TypeScript: Type safety
  • class-variance-authority (CVA): Variant management
  • clsx: Class name concatenation
  • tailwind-merge: Conflict-free class merging

Radix UI Integration:

tsx
1// shadcn/ui components wrap Radix primitives 2import * as DialogPrimitive from "@radix-ui/react-dialog" 3 4// Add styling and variants 5const Dialog = DialogPrimitive.Root 6const DialogTrigger = DialogPrimitive.Trigger 7const DialogContent = React.forwardRef<...>( 8 ({ className, children, ...props }, ref) => ( 9 <DialogPrimitive.Content 10 ref={ref} 11 className={cn("fixed ...", className)} 12 {...props} 13 /> 14 ) 15)

Configuration

components.json

json
1{ 2 "$schema": "https://ui.shadcn.com/schema.json", 3 "style": "default", 4 "rsc": true, 5 "tsx": true, 6 "tailwind": { 7 "config": "tailwind.config.ts", 8 "css": "app/globals.css", 9 "baseColor": "slate", 10 "cssVariables": true, 11 "prefix": "" 12 }, 13 "aliases": { 14 "components": "@/components", 15 "utils": "@/lib/utils", 16 "ui": "@/components/ui", 17 "lib": "@/lib", 18 "hooks": "@/hooks" 19 } 20}

Key Options:

  • style: "default" or "new-york" (design variants)
  • rsc: React Server Components support
  • cssVariables: Use CSS variables for theming
  • prefix: Tailwind class prefix (optional)

Tailwind Configuration

ts
1// tailwind.config.ts 2import type { Config } from "tailwindcss" 3 4const config = { 5 darkMode: ["class"], 6 content: [ 7 './pages/**/*.{ts,tsx}', 8 './components/**/*.{ts,tsx}', 9 './app/**/*.{ts,tsx}', 10 './src/**/*.{ts,tsx}', 11 ], 12 prefix: "", 13 theme: { 14 container: { 15 center: true, 16 padding: "2rem", 17 screens: { 18 "2xl": "1400px", 19 }, 20 }, 21 extend: { 22 colors: { 23 border: "hsl(var(--border))", 24 input: "hsl(var(--input))", 25 ring: "hsl(var(--ring))", 26 background: "hsl(var(--background))", 27 foreground: "hsl(var(--foreground))", 28 primary: { 29 DEFAULT: "hsl(var(--primary))", 30 foreground: "hsl(var(--primary-foreground))", 31 }, 32 secondary: { 33 DEFAULT: "hsl(var(--secondary))", 34 foreground: "hsl(var(--secondary-foreground))", 35 }, 36 destructive: { 37 DEFAULT: "hsl(var(--destructive))", 38 foreground: "hsl(var(--destructive-foreground))", 39 }, 40 muted: { 41 DEFAULT: "hsl(var(--muted))", 42 foreground: "hsl(var(--muted-foreground))", 43 }, 44 accent: { 45 DEFAULT: "hsl(var(--accent))", 46 foreground: "hsl(var(--accent-foreground))", 47 }, 48 popover: { 49 DEFAULT: "hsl(var(--popover))", 50 foreground: "hsl(var(--popover-foreground))", 51 }, 52 card: { 53 DEFAULT: "hsl(var(--card))", 54 foreground: "hsl(var(--card-foreground))", 55 }, 56 }, 57 borderRadius: { 58 lg: "var(--radius)", 59 md: "calc(var(--radius) - 2px)", 60 sm: "calc(var(--radius) - 4px)", 61 }, 62 keyframes: { 63 "accordion-down": { 64 from: { height: "0" }, 65 to: { height: "var(--radix-accordion-content-height)" }, 66 }, 67 "accordion-up": { 68 from: { height: "var(--radix-accordion-content-height)" }, 69 to: { height: "0" }, 70 }, 71 }, 72 animation: { 73 "accordion-down": "accordion-down 0.2s ease-out", 74 "accordion-up": "accordion-up 0.2s ease-out", 75 }, 76 }, 77 }, 78 plugins: [require("tailwindcss-animate")], 79} satisfies Config 80 81export default config

CSS Variables (globals.css)

css
1@tailwind base; 2@tailwind components; 3@tailwind utilities; 4 5@layer base { 6 :root { 7 --background: 0 0% 100%; 8 --foreground: 222.2 84% 4.9%; 9 --card: 0 0% 100%; 10 --card-foreground: 222.2 84% 4.9%; 11 --popover: 0 0% 100%; 12 --popover-foreground: 222.2 84% 4.9%; 13 --primary: 222.2 47.4% 11.2%; 14 --primary-foreground: 210 40% 98%; 15 --secondary: 210 40% 96.1%; 16 --secondary-foreground: 222.2 47.4% 11.2%; 17 --muted: 210 40% 96.1%; 18 --muted-foreground: 215.4 16.3% 46.9%; 19 --accent: 210 40% 96.1%; 20 --accent-foreground: 222.2 47.4% 11.2%; 21 --destructive: 0 84.2% 60.2%; 22 --destructive-foreground: 210 40% 98%; 23 --border: 214.3 31.8% 91.4%; 24 --input: 214.3 31.8% 91.4%; 25 --ring: 222.2 84% 4.9%; 26 --radius: 0.5rem; 27 } 28 29 .dark { 30 --background: 222.2 84% 4.9%; 31 --foreground: 210 40% 98%; 32 --card: 222.2 84% 4.9%; 33 --card-foreground: 210 40% 98%; 34 --popover: 222.2 84% 4.9%; 35 --popover-foreground: 210 40% 98%; 36 --primary: 210 40% 98%; 37 --primary-foreground: 222.2 47.4% 11.2%; 38 --secondary: 217.2 32.6% 17.5%; 39 --secondary-foreground: 210 40% 98%; 40 --muted: 217.2 32.6% 17.5%; 41 --muted-foreground: 215 20.2% 65.1%; 42 --accent: 217.2 32.6% 17.5%; 43 --accent-foreground: 210 40% 98%; 44 --destructive: 0 62.8% 30.6%; 45 --destructive-foreground: 210 40% 98%; 46 --border: 217.2 32.6% 17.5%; 47 --input: 217.2 32.6% 17.5%; 48 --ring: 212.7 26.8% 83.9%; 49 } 50} 51 52@layer base { 53 * { 54 @apply border-border; 55 } 56 body { 57 @apply bg-background text-foreground; 58 } 59}

Component Catalog

Button

tsx
1import { Button } from "@/components/ui/button" 2 3// Variants 4<Button variant="default">Default</Button> 5<Button variant="destructive">Destructive</Button> 6<Button variant="outline">Outline</Button> 7<Button variant="secondary">Secondary</Button> 8<Button variant="ghost">Ghost</Button> 9<Button variant="link">Link</Button> 10 11// Sizes 12<Button size="default">Default</Button> 13<Button size="sm">Small</Button> 14<Button size="lg">Large</Button> 15<Button size="icon"><Icon /></Button> 16 17// States 18<Button disabled>Disabled</Button> 19<Button asChild> 20 <Link href="/about">As Link</Link> 21</Button>

Implementation Pattern (CVA):

tsx
1import { cva, type VariantProps } from "class-variance-authority" 2 3const buttonVariants = cva( 4 "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50", 5 { 6 variants: { 7 variant: { 8 default: "bg-primary text-primary-foreground hover:bg-primary/90", 9 destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", 10 outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 11 secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", 12 ghost: "hover:bg-accent hover:text-accent-foreground", 13 link: "text-primary underline-offset-4 hover:underline", 14 }, 15 size: { 16 default: "h-10 px-4 py-2", 17 sm: "h-9 rounded-md px-3", 18 lg: "h-11 rounded-md px-8", 19 icon: "h-10 w-10", 20 }, 21 }, 22 defaultVariants: { 23 variant: "default", 24 size: "default", 25 }, 26 } 27)

Card

tsx
1import { 2 Card, 3 CardHeader, 4 CardFooter, 5 CardTitle, 6 CardDescription, 7 CardContent, 8} from "@/components/ui/card" 9 10<Card> 11 <CardHeader> 12 <CardTitle>Card Title</CardTitle> 13 <CardDescription>Card description goes here</CardDescription> 14 </CardHeader> 15 <CardContent> 16 <p>Card content</p> 17 </CardContent> 18 <CardFooter> 19 <Button>Action</Button> 20 </CardFooter> 21</Card>

Dialog (Modal)

tsx
1import { 2 Dialog, 3 DialogContent, 4 DialogDescription, 5 DialogHeader, 6 DialogTitle, 7 DialogTrigger, 8 DialogFooter, 9} from "@/components/ui/dialog" 10 11<Dialog> 12 <DialogTrigger asChild> 13 <Button>Open Dialog</Button> 14 </DialogTrigger> 15 <DialogContent> 16 <DialogHeader> 17 <DialogTitle>Are you sure?</DialogTitle> 18 <DialogDescription> 19 This action cannot be undone. 20 </DialogDescription> 21 </DialogHeader> 22 <DialogFooter> 23 <Button variant="outline">Cancel</Button> 24 <Button>Confirm</Button> 25 </DialogFooter> 26 </DialogContent> 27</Dialog>

Form (with react-hook-form + zod)

tsx
1import { zodResolver } from "@hookform/resolvers/zod" 2import { useForm } from "react-hook-form" 3import * as z from "zod" 4import { 5 Form, 6 FormControl, 7 FormDescription, 8 FormField, 9 FormItem, 10 FormLabel, 11 FormMessage, 12} from "@/components/ui/form" 13import { Input } from "@/components/ui/input" 14import { Button } from "@/components/ui/button" 15 16const formSchema = z.object({ 17 username: z.string().min(2, { 18 message: "Username must be at least 2 characters.", 19 }), 20 email: z.string().email({ 21 message: "Please enter a valid email address.", 22 }), 23}) 24 25function ProfileForm() { 26 const form = useForm<z.infer<typeof formSchema>>({ 27 resolver: zodResolver(formSchema), 28 defaultValues: { 29 username: "", 30 email: "", 31 }, 32 }) 33 34 function onSubmit(values: z.infer<typeof formSchema>) { 35 console.log(values) 36 } 37 38 return ( 39 <Form {...form}> 40 <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> 41 <FormField 42 control={form.control} 43 name="username" 44 render={({ field }) => ( 45 <FormItem> 46 <FormLabel>Username</FormLabel> 47 <FormControl> 48 <Input placeholder="shadcn" {...field} /> 49 </FormControl> 50 <FormDescription> 51 This is your public display name. 52 </FormDescription> 53 <FormMessage /> 54 </FormItem> 55 )} 56 /> 57 <FormField 58 control={form.control} 59 name="email" 60 render={({ field }) => ( 61 <FormItem> 62 <FormLabel>Email</FormLabel> 63 <FormControl> 64 <Input type="email" placeholder="user@example.com" {...field} /> 65 </FormControl> 66 <FormMessage /> 67 </FormItem> 68 )} 69 /> 70 <Button type="submit">Submit</Button> 71 </form> 72 </Form> 73 ) 74}

Table

tsx
1import { 2 Table, 3 TableBody, 4 TableCaption, 5 TableCell, 6 TableHead, 7 TableHeader, 8 TableRow, 9} from "@/components/ui/table" 10 11<Table> 12 <TableCaption>A list of your recent invoices.</TableCaption> 13 <TableHeader> 14 <TableRow> 15 <TableHead>Invoice</TableHead> 16 <TableHead>Status</TableHead> 17 <TableHead>Method</TableHead> 18 <TableHead className="text-right">Amount</TableHead> 19 </TableRow> 20 </TableHeader> 21 <TableBody> 22 <TableRow> 23 <TableCell>INV001</TableCell> 24 <TableCell>Paid</TableCell> 25 <TableCell>Credit Card</TableCell> 26 <TableCell className="text-right">$250.00</TableCell> 27 </TableRow> 28 </TableBody> 29</Table>

Additional Components

Available via CLI:

  • accordion - Collapsible content sections
  • alert - Contextual feedback messages
  • alert-dialog - Interrupting modal dialogs
  • avatar - User profile images
  • badge - Status indicators
  • calendar - Date picker
  • checkbox - Binary input
  • command - Command palette (⌘K menu)
  • context-menu - Right-click menus
  • dropdown-menu - Dropdown menus
  • hover-card - Hover tooltips
  • input - Text input
  • label - Form labels
  • menubar - Application menu bar
  • navigation-menu - Site navigation
  • popover - Floating panels
  • progress - Progress indicators
  • radio-group - Radio button groups
  • scroll-area - Custom scrollbars
  • select - Dropdown selects
  • separator - Visual dividers
  • sheet - Side panels
  • skeleton - Loading placeholders
  • slider - Range input
  • switch - Toggle switch
  • tabs - Tab navigation
  • textarea - Multi-line input
  • toast - Notification toasts
  • toggle - Toggle button
  • tooltip - Hover tooltips

Theming

Color Customization

Change base color scheme:

bash
1# Regenerate components with new base color 2npx shadcn-ui@latest init 3 4# Choose new base: Slate, Gray, Zinc, Neutral, Stone

Manual color override (globals.css):

css
1:root { 2 --primary: 210 100% 50%; /* HSL: Blue */ 3 --primary-foreground: 0 0% 100%; 4} 5 6.dark { 7 --primary: 210 100% 60%; /* Lighter blue for dark mode */ 8}

Custom Variants

tsx
1// Extend button variants 2const buttonVariants = cva( 3 "...", 4 { 5 variants: { 6 variant: { 7 // ...existing variants 8 gradient: "bg-gradient-to-r from-purple-500 to-pink-500 text-white", 9 }, 10 }, 11 } 12) 13 14// Usage 15<Button variant="gradient">Gradient Button</Button>

Theme Switching

tsx
1// Using next-themes 2import { ThemeProvider } from "next-themes" 3 4// app/layout.tsx 5export default function RootLayout({ children }) { 6 return ( 7 <html lang="en" suppressHydrationWarning> 8 <body> 9 <ThemeProvider 10 attribute="class" 11 defaultTheme="system" 12 enableSystem 13 disableTransitionOnChange 14 > 15 {children} 16 </ThemeProvider> 17 </body> 18 </html> 19 ) 20} 21 22// Theme toggle component 23import { Moon, Sun } from "lucide-react" 24import { useTheme } from "next-themes" 25import { Button } from "@/components/ui/button" 26 27export function ThemeToggle() { 28 const { setTheme, theme } = useTheme() 29 30 return ( 31 <Button 32 variant="ghost" 33 size="icon" 34 onClick={() => setTheme(theme === "light" ? "dark" : "light")} 35 > 36 <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /> 37 <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" /> 38 <span className="sr-only">Toggle theme</span> 39 </Button> 40 ) 41}

Dark Mode

Setup with Next.js

bash
1npm install next-themes
tsx
1// app/providers.tsx 2"use client" 3 4import { ThemeProvider } from "next-themes" 5 6export function Providers({ children }: { children: React.ReactNode }) { 7 return ( 8 <ThemeProvider attribute="class" defaultTheme="system" enableSystem> 9 {children} 10 </ThemeProvider> 11 ) 12} 13 14// app/layout.tsx 15import { Providers } from "./providers" 16 17export default function RootLayout({ children }) { 18 return ( 19 <html lang="en" suppressHydrationWarning> 20 <body> 21 <Providers>{children}</Providers> 22 </body> 23 </html> 24 ) 25}

Dark Mode Utilities

tsx
1// Force dark mode for specific section 2<div className="dark"> 3 <Card>Always dark, regardless of theme</Card> 4</div> 5 6// Conditional styling 7<div className="bg-white dark:bg-slate-950"> 8 <p className="text-slate-900 dark:text-slate-50"> 9 Adapts to theme 10 </p> 11</div>

Next.js Integration

App Router Setup

bash
1# Create Next.js app with TypeScript and Tailwind 2npx create-next-app@latest my-app --typescript --tailwind --app 3 4# Initialize shadcn/ui 5cd my-app 6npx shadcn-ui@latest init 7 8# Add components 9npx shadcn-ui@latest add button card form

Server Components

tsx
1// app/page.tsx (Server Component by default) 2import { Button } from "@/components/ui/button" 3 4export default function HomePage() { 5 return ( 6 <main> 7 <h1>Welcome</h1> 8 {/* Static components work in Server Components */} 9 <Button asChild> 10 <a href="/about">Learn More</a> 11 </Button> 12 </main> 13 ) 14}

Client Components

tsx
1// app/interactive.tsx 2"use client" 3 4import { useState } from "react" 5import { Button } from "@/components/ui/button" 6import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog" 7 8export function InteractiveSection() { 9 const [open, setOpen] = useState(false) 10 11 return ( 12 <Dialog open={open} onOpenChange={setOpen}> 13 <DialogTrigger asChild> 14 <Button>Open Dialog</Button> 15 </DialogTrigger> 16 <DialogContent> 17 <p>Client-side interactivity</p> 18 </DialogContent> 19 </Dialog> 20 ) 21}

Route Handlers

tsx
1// app/api/submit/route.ts 2import { NextResponse } from "next/server" 3import { z } from "zod" 4 5const formSchema = z.object({ 6 email: z.string().email(), 7 message: z.string().min(10), 8}) 9 10export async function POST(request: Request) { 11 try { 12 const body = await request.json() 13 const validatedData = formSchema.parse(body) 14 15 // Process form data 16 return NextResponse.json({ success: true }) 17 } catch (error) { 18 if (error instanceof z.ZodError) { 19 return NextResponse.json({ errors: error.errors }, { status: 400 }) 20 } 21 return NextResponse.json({ error: "Internal error" }, { status: 500 }) 22 } 23}

Accessibility

ARIA Support

All shadcn/ui components include proper ARIA attributes via Radix UI:

tsx
1// Dialog automatically includes: 2// - role="dialog" 3// - aria-describedby 4// - aria-labelledby 5// - Focus trap 6// - Escape key handler 7<Dialog> 8 <DialogContent> 9 {/* Automatically accessible */} 10 </DialogContent> 11</Dialog> 12 13// Button includes: 14// - role="button" 15// - tabindex="0" 16// - Keyboard activation (Space/Enter) 17<Button>Accessible by default</Button>

Keyboard Navigation

Built-in keyboard support:

  • Tab / Shift+Tab - Navigate between interactive elements
  • Enter / Space - Activate buttons
  • Escape - Close dialogs, dropdowns, popovers
  • Arrow keys - Navigate menus, select options, radio groups
  • Home / End - Jump to first/last in lists

Example: Command Palette:

tsx
1import { 2 Command, 3 CommandDialog, 4 CommandInput, 5 CommandList, 6 CommandEmpty, 7 CommandGroup, 8 CommandItem, 9} from "@/components/ui/command" 10 11// ⌘K to open 12<CommandDialog open={open} onOpenChange={setOpen}> 13 <CommandInput placeholder="Type a command..." /> 14 <CommandList> 15 <CommandEmpty>No results found.</CommandEmpty> 16 <CommandGroup heading="Suggestions"> 17 <CommandItem>Calendar</CommandItem> 18 <CommandItem>Search Emoji</CommandItem> 19 <CommandItem>Calculator</CommandItem> 20 </CommandGroup> 21 </CommandList> 22</CommandDialog>

Screen Reader Support

tsx
1// Visually hidden but accessible to screen readers 2<span className="sr-only">Close dialog</span> 3 4// Skip navigation links 5<a href="#main-content" className="sr-only focus:not-sr-only"> 6 Skip to main content 7</a> 8 9// Descriptive labels 10<FormLabel htmlFor="email">Email address</FormLabel> 11<Input 12 id="email" 13 type="email" 14 aria-describedby="email-description" 15 aria-invalid={!!errors.email} 16/> 17<FormDescription id="email-description"> 18 We'll never share your email. 19</FormDescription>

Focus Management

tsx
1// Focus trap in Dialog (automatic) 2<Dialog> 3 <DialogContent> 4 {/* Focus stays within dialog until closed */} 5 </DialogContent> 6</Dialog> 7 8// Custom focus management 9import { useRef, useEffect } from "react" 10 11function CustomComponent() { 12 const inputRef = useRef<HTMLInputElement>(null) 13 14 useEffect(() => { 15 inputRef.current?.focus() 16 }, []) 17 18 return <Input ref={inputRef} /> 19}

Composition Patterns

Compound Components

tsx
1// Card composition 2<Card> 3 <CardHeader> 4 <CardTitle>Title</CardTitle> 5 <CardDescription>Description</CardDescription> 6 </CardHeader> 7 <CardContent>Content</CardContent> 8 <CardFooter>Footer</CardFooter> 9</Card> 10 11// Form composition 12<Form {...form}> 13 <FormField 14 control={form.control} 15 name="field" 16 render={({ field }) => ( 17 <FormItem> 18 <FormLabel>Label</FormLabel> 19 <FormControl> 20 <Input {...field} /> 21 </FormControl> 22 <FormDescription>Help text</FormDescription> 23 <FormMessage /> 24 </FormItem> 25 )} 26 /> 27</Form>

Polymorphic Components (asChild)

tsx
1// Render Button as Link 2import { Button } from "@/components/ui/button" 3import Link from "next/link" 4 5<Button asChild> 6 <Link href="/dashboard">Go to Dashboard</Link> 7</Button> 8 9// Render as custom component 10<Button asChild> 11 <motion.button 12 whileHover={{ scale: 1.05 }} 13 whileTap={{ scale: 0.95 }} 14 > 15 Animated Button 16 </motion.button> 17</Button>

How it works (Radix Slot):

tsx
1import { Slot } from "@radix-ui/react-slot" 2 3interface ButtonProps { 4 asChild?: boolean 5} 6 7const Button = ({ asChild, ...props }: ButtonProps) => { 8 const Comp = asChild ? Slot : "button" 9 return <Comp {...props} /> 10}

Custom Compositions

tsx
1// Create custom card variant 2export function PricingCard({ 3 title, 4 price, 5 features, 6 highlighted 7}: PricingCardProps) { 8 return ( 9 <Card className={cn(highlighted && "border-primary")}> 10 <CardHeader> 11 <CardTitle>{title}</CardTitle> 12 <CardDescription className="text-3xl font-bold"> 13 ${price}/mo 14 </CardDescription> 15 </CardHeader> 16 <CardContent> 17 <ul className="space-y-2"> 18 {features.map((feature) => ( 19 <li key={feature} className="flex items-center"> 20 <Check className="mr-2 h-4 w-4 text-primary" /> 21 {feature} 22 </li> 23 ))} 24 </ul> 25 </CardContent> 26 <CardFooter> 27 <Button className="w-full" variant={highlighted ? "default" : "outline"}> 28 Get Started 29 </Button> 30 </CardFooter> 31 </Card> 32 ) 33}

CLI Commands

Initialize

bash
1# Interactive init 2npx shadcn-ui@latest init 3 4# Non-interactive with defaults 5npx shadcn-ui@latest init -y 6 7# Specify options 8npx shadcn-ui@latest init --typescript --tailwind

Add Components

bash
1# Single component 2npx shadcn-ui@latest add button 3 4# Multiple components 5npx shadcn-ui@latest add button card dialog form 6 7# All components (not recommended - adds everything) 8npx shadcn-ui@latest add --all 9 10# Specific version 11npx shadcn-ui@latest add button@1.0.0 12 13# Overwrite existing 14npx shadcn-ui@latest add button --overwrite 15 16# Different path 17npx shadcn-ui@latest add button --path src/components/ui

Diff Components

bash
1# Check for component updates 2npx shadcn-ui@latest diff 3 4# Diff specific component 5npx shadcn-ui@latest diff button 6 7# Show what would change 8npx shadcn-ui@latest diff --check

Update Components

bash
1# Update all components 2npx shadcn-ui@latest update 3 4# Update specific components 5npx shadcn-ui@latest update button card 6 7# Preview changes before applying 8npx shadcn-ui@latest update --dry-run

Advanced Patterns

Custom Hooks

tsx
1// useToast hook (built-in with toast component) 2import { useToast } from "@/components/ui/use-toast" 3 4function MyComponent() { 5 const { toast } = useToast() 6 7 return ( 8 <Button 9 onClick={() => { 10 toast({ 11 title: "Scheduled: Catch up", 12 description: "Friday, February 10, 2023 at 5:57 PM", 13 }) 14 }} 15 > 16 Show Toast 17 </Button> 18 ) 19} 20 21// Custom form hook 22import { useForm } from "react-hook-form" 23import { zodResolver } from "@hookform/resolvers/zod" 24 25function useFormWithToast<T extends z.ZodType>(schema: T) { 26 const { toast } = useToast() 27 const form = useForm({ 28 resolver: zodResolver(schema), 29 }) 30 31 const handleSubmit = form.handleSubmit(async (data) => { 32 try { 33 // Submit logic 34 toast({ title: "Success!" }) 35 } catch (error) { 36 toast({ title: "Error", variant: "destructive" }) 37 } 38 }) 39 40 return { form, handleSubmit } 41}

Responsive Design

tsx
1// Mobile-first responsive components 2<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> 3 <Card>Mobile: 1 col, Tablet: 2 col, Desktop: 3 col</Card> 4</div> 5 6// Responsive dialog (sheet on mobile, dialog on desktop) 7import { useMediaQuery } from "@/hooks/use-media-query" 8import { Dialog, DialogContent } from "@/components/ui/dialog" 9import { Sheet, SheetContent } from "@/components/ui/sheet" 10 11function ResponsiveModal({ children, ...props }) { 12 const isDesktop = useMediaQuery("(min-width: 768px)") 13 14 if (isDesktop) { 15 return ( 16 <Dialog {...props}> 17 <DialogContent>{children}</DialogContent> 18 </Dialog> 19 ) 20 } 21 22 return ( 23 <Sheet {...props}> 24 <SheetContent>{children}</SheetContent> 25 </Sheet> 26 ) 27}

Animation Variants

tsx
1// Using Framer Motion with shadcn/ui 2import { motion } from "framer-motion" 3import { Card } from "@/components/ui/card" 4 5const MotionCard = motion(Card) 6 7<MotionCard 8 initial={{ opacity: 0, y: 20 }} 9 animate={{ opacity: 1, y: 0 }} 10 transition={{ duration: 0.3 }} 11> 12 Animated Card 13</MotionCard> 14 15// Staggered list animation 16const container = { 17 hidden: { opacity: 0 }, 18 show: { 19 opacity: 1, 20 transition: { 21 staggerChildren: 0.1 22 } 23 } 24} 25 26const item = { 27 hidden: { opacity: 0, y: 20 }, 28 show: { opacity: 1, y: 0 } 29} 30 31<motion.ul variants={container} initial="hidden" animate="show"> 32 {items.map((item) => ( 33 <motion.li key={item.id} variants={item}> 34 <Card>{item.content}</Card> 35 </motion.li> 36 ))} 37</motion.ul>

Best Practices

Code Organization:

  • Keep shadcn/ui components in components/ui/ (don't mix with app components)
  • Create custom compositions in components/ (outside ui/)
  • Use lib/utils.ts for shared utilities

Customization:

  • Modify components directly in your project (you own the code)
  • Use CSS variables for theme-wide changes
  • Extend variants with CVA for new styles
  • Don't edit components.json manually (use CLI)

Performance:

  • Tree-shaking automatic (only imports what you use)
  • Use asChild to avoid unnecessary wrapper elements
  • Lazy load heavy components (Calendar, Command)
  • Prefer Server Components when possible (Next.js)

Accessibility:

  • Don't remove ARIA attributes from components
  • Test keyboard navigation for custom compositions
  • Maintain focus management in dialogs/modals
  • Use semantic HTML with asChild when applicable

TypeScript:

  • Leverage exported types (ButtonProps, CardProps, etc.)
  • Use VariantProps for variant type safety
  • Add strict null checks for form validation

Troubleshooting

Import errors:

bash
1# Check path aliases in tsconfig.json 2{ 3 "compilerOptions": { 4 "baseUrl": ".", 5 "paths": { 6 "@/*": ["./src/*"] 7 } 8 } 9}

Tailwind classes not applying:

ts
1// Ensure content paths include your components 2// tailwind.config.ts 3content: [ 4 './src/components/**/*.{ts,tsx}', // Add this 5 './src/app/**/*.{ts,tsx}', 6]

Dark mode not working:

tsx
1// Add suppressHydrationWarning to <html> 2<html lang="en" suppressHydrationWarning>

Form validation not triggering:

tsx
1// Ensure FormMessage is included in FormField 2<FormField> 3 <FormItem> 4 <FormControl>...</FormControl> 5 <FormMessage /> {/* Required for errors */} 6 </FormItem> 7</FormField>

Resources

相关技能

寻找 shadcn-ui 的替代方案 (Alternative) 或可搭配使用的同类 community Skill?探索以下相关开源技能。

查看全部

openclaw-release-maintainer

Logo of openclaw
openclaw

Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

333.8k
0
AI

widget-generator

Logo of f
f

Generate customizable widget plugins for the prompts.chat feed system

149.6k
0
AI

flags

Logo of vercel
vercel

The React Framework

138.4k
0
浏览器

pr-review

Logo of pytorch
pytorch

Tensors and Dynamic neural networks in Python with strong GPU acceleration

98.6k
0
开发者工具