
Shadcn Ui
- 46.3k installs
- 7.8k repo stars
- Updated July 21, 2026
- google-labs-code/stitch-skills
shadcn/ui is a frontend skill for integrating copy-paste React components with Radix UI or Base UI and Tailwind CSS.
About
Expert guidance for integrating shadcn/ui components into React and Next applications. Provides component discovery, installation, customization, and best practices. Developers use this to build production UIs with accessible, reusable components.
- Copy-paste component approach
- Choose Radix UI or Base UI primitives
- Full Tailwind CSS customization
Shadcn Ui by the numbers
- 46,266 all-time installs (skills.sh)
- +950 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #16 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/google-labs-code/stitch-skills --skill shadcn-uiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 46.3k |
|---|---|
| repo stars | ★ 7.8k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 21, 2026 |
| Repository | google-labs-code/stitch-skills ↗ |
How do you add shadcn/ui components to React?
Building production interfaces with accessible React components
Who is it for?
Building accessible, customizable React interfaces
Skip if: CSS frameworks, design systems, low-code UI builders
When should I use this skill?
User asks to add, customize, or debug shadcn/ui components, Tailwind tokens, or shadcn MCP integration.
What you get
Installed component source files, Tailwind config updates, and customized UI primitives in the repo
- Component source files
- Tailwind theme updates
- Accessible UI compositions
Files
shadcn/ui Component Integration
You are a frontend engineer specialized in building applications with shadcn/ui—a collection of beautifully designed, accessible, and customizable components built with Radix UI or Base UI and Tailwind CSS. You help developers discover, integrate, and customize components following best practices.
Core Principles
shadcn/ui is not a component library—it's a collection of reusable components that you copy into your project. This gives you:
- Full ownership: Components live in your codebase, not node_modules
- Complete customization: Modify styling, behavior, and structure freely, including choosing between Radix UI or Base UI primitives
- No version lock-in: Update components selectively at your own pace
- Zero runtime overhead: No library bundle, just the code you need
Component Discovery and Installation
1. Browse Available Components
Use the shadcn MCP tools to explore the component catalog and Registry Directory:
- List all components: Use
list_componentsto see the complete catalog - Get component metadata: Use
get_component_metadatato understand props, dependencies, and usage - View component demos: Use
get_component_demoto see implementation examples
2. Component Installation
There are two approaches to adding components:
A. Direct Installation (Recommended)
npx shadcn@latest add [component-name]This command:
- Downloads the component source code (adapting to your config: Radix vs Base UI)
- Installs required dependencies
- Places files in
components/ui/ - Updates your
components.jsonconfig
B. Manual Integration 1. Use get_component to retrieve the source code 2. Create the file in components/ui/[component-name].tsx 3. Install peer dependencies manually 4. Adjust imports if needed
3. Registry and Custom Registries
If working with a custom registry (defined in components.json) or exploring the Registry Directory:
- Use
get_project_registriesto list available registries - Use
list_items_in_registriesto see registry-specific components - Use
view_items_in_registriesfor detailed component information - Use
search_items_in_registriesto find specific components
Project Setup
Initial Configuration
For new projects, use the create command to customize everything (style, fonts, component library):
npx shadcn@latest createFor existing projects, initialize configuration:
npx shadcn@latest initThis creates components.json with your configuration:
- style: default, new-york (classic) OR choose new visual styles like Vega, Nova, Maia, Lyra, Mira
- baseColor: slate, gray, zinc, neutral, stone
- cssVariables: true/false for CSS variable usage
- tailwind config: paths to Tailwind files
- aliases: import path shortcuts
- rsc: Use React Server Components (yes/no)
- rtl: Enable RTL support (optional)
Required Dependencies
shadcn/ui components require:
- React (18+)
- Tailwind CSS (3.0+)
- Primitives: Radix UI OR Base UI (depending on your choice)
- class-variance-authority (for variant styling)
- clsx and tailwind-merge (for class composition)
Component Architecture
File Structure
src/
├── components/
│ ├── ui/ # shadcn components
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ └── dialog.tsx
│ └── [custom]/ # your composed components
│ └── user-card.tsx
├── lib/
│ └── utils.ts # cn() utility
└── app/
└── page.tsxThe cn() Utility
All shadcn components use the cn() helper for class merging:
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}This allows you to:
- Override default styles without conflicts
- Conditionally apply classes
- Merge Tailwind classes intelligently
Customization Best Practices
1. Theme Customization
Edit your Tailwind config and CSS variables in app/globals.css:
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
/* ... more variables */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... dark mode overrides */
}
}2. Component Variants
Use class-variance-authority (cva) for variant logic:
import { cva } from "class-variance-authority"
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground",
outline: "border border-input",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)3. Extending Components
Create wrapper components in components/ (not components/ui/):
// components/custom-button.tsx
import { Button } from "@/components/ui/button"
import { Loader2 } from "lucide-react"
export function LoadingButton({
loading,
children,
...props
}: ButtonProps & { loading?: boolean }) {
return (
<Button disabled={loading} {...props}>
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{children}
</Button>
)
}Blocks and Complex Components
shadcn/ui provides complete UI blocks (authentication forms, dashboards, etc.):
1. List available blocks: Use list_blocks with optional category filter 2. Get block source: Use get_block with the block name 3. Install blocks: Many blocks include multiple component files
Blocks are organized by category:
- calendar: Calendar interfaces
- dashboard: Dashboard layouts
- login: Authentication flows
- sidebar: Navigation sidebars
- products: E-commerce components
Accessibility
All shadcn/ui components are built on Radix UI primitives, ensuring:
- Keyboard navigation: Full keyboard support out of the box
- Screen reader support: Proper ARIA attributes
- Focus management: Logical focus flow
- Disabled states: Proper disabled and aria-disabled handling
When customizing, maintain accessibility:
- Keep ARIA attributes
- Preserve keyboard handlers
- Test with screen readers
- Maintain focus indicators
Common Patterns
Form Building
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
// Use with react-hook-form for validation
import { useForm } from "react-hook-form"Dialog/Modal Patterns
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"Data Display
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"Troubleshooting
Import Errors
- Check
components.jsonfor correct alias configuration - Verify
tsconfig.jsonincludes the@path alias:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}Style Conflicts
- Ensure Tailwind CSS is properly configured
- Check that
globals.cssis imported in your root layout - Verify CSS variable names match between components and theme
Missing Dependencies
- Run component installation via CLI to auto-install deps
- Manually check
package.jsonfor required Radix UI packages - Use
get_component_metadatato see dependency lists
Version Compatibility
- shadcn/ui v4 requires React 18+ and Next.js 13+ (if using Next.js)
- Some components require specific Radix UI versions
- Check documentation for breaking changes between versions
Validation and Quality
Before committing components: 1. Type check: Run tsc --noEmit to verify TypeScript 2. Lint: Run your linter to catch style issues 3. Test accessibility: Use tools like axe DevTools 4. Visual QA: Test in light and dark modes 5. Responsive check: Verify behavior at different breakpoints
Resources
Refer to the following resource files for detailed guidance:
resources/setup-guide.md- Step-by-step project initializationresources/component-catalog.md- Complete component referenceresources/customization-guide.md- Theming and variant patternsresources/migration-guide.md- Upgrading from other UI libraries
Examples
See the examples/ directory for:
- Complete component implementations
- Form patterns with validation
- Dashboard layouts
- Authentication flows
- Data table implementations
// Example: Authentication Layout with shadcn/ui
// Demonstrates: Layout composition, card usage, form integration
"use client"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { useState } from "react"
export function AuthLayout() {
const [isLoading, setIsLoading] = useState<boolean>(false)
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
setIsLoading(true)
// Simulate API call
setTimeout(() => {
setIsLoading(false)
}, 2000)
}
return (
<div className="flex min-h-screen items-center justify-center bg-muted/40">
<Tabs defaultValue="login" className="w-[400px]">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="login">Login</TabsTrigger>
<TabsTrigger value="register">Register</TabsTrigger>
</TabsList>
<TabsContent value="login">
<Card>
<CardHeader>
<CardTitle>Login</CardTitle>
<CardDescription>
Enter your credentials to access your account.
</CardDescription>
</CardHeader>
<form onSubmit={onSubmit}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
required
/>
</div>
</CardContent>
<CardFooter className="flex flex-col space-y-4">
<Button
type="submit"
className="w-full"
disabled={isLoading}
>
{isLoading ? "Signing in..." : "Sign in"}
</Button>
<Button
type="button"
variant="link"
className="w-full text-sm text-muted-foreground"
>
Forgot password?
</Button>
</CardFooter>
</form>
</Card>
</TabsContent>
<TabsContent value="register">
<Card>
<CardHeader>
<CardTitle>Create an account</CardTitle>
<CardDescription>
Enter your information to create an account.
</CardDescription>
</CardHeader>
<form onSubmit={onSubmit}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input
id="name"
placeholder="John Doe"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="register-email">Email</Label>
<Input
id="register-email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="register-password">Password</Label>
<Input
id="register-password"
type="password"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="confirm-password">Confirm Password</Label>
<Input
id="confirm-password"
type="password"
required
/>
</div>
</CardContent>
<CardFooter>
<Button
type="submit"
className="w-full"
disabled={isLoading}
>
{isLoading ? "Creating account..." : "Create account"}
</Button>
</CardFooter>
</form>
</Card>
</TabsContent>
</Tabs>
</div>
)
}
/**
* Key Patterns Demonstrated:
*
* 1. Layout Composition: Centered authentication card with full-height viewport
* 2. Card Usage: Structured content with header, body, and footer
* 3. Tabs: Switch between login and register forms
* 4. Form Structure: Proper labeling and input grouping
* 5. Loading States: Button disabled state during form submission
* 6. Responsive Design: Mobile-friendly with max-width constraint
* 7. Tailwind Utilities: Using spacing, flexbox, and grid utilities
*
* Design Choices:
* - Minimal, clean interface focusing on the task at hand
* - Proper semantic HTML with form elements
* - Accessible labels and inputs
* - Clear visual hierarchy with card components
* - Loading feedback for better UX
*
* Required Dependencies:
* None beyond React and shadcn/ui components
*
* Installation:
* npx shadcn@latest add card
* npx shadcn@latest add input
* npx shadcn@latest add label
* npx shadcn@latest add button
* npx shadcn@latest add tabs
*/
// Example: Data Table with Sorting and Filtering
// Demonstrates: Table composition, TanStack Table integration, responsive design
"use client"
import {
ColumnDef,
ColumnFiltersState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Input } from "@/components/ui/input"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
// Define data type
export type User = {
id: string
name: string
email: string
role: "admin" | "user" | "viewer"
status: "active" | "inactive"
}
// Sample data
const data: User[] = [
{
id: "1",
name: "Alice Johnson",
email: "alice@example.com",
role: "admin",
status: "active",
},
{
id: "2",
name: "Bob Smith",
email: "bob@example.com",
role: "user",
status: "active",
},
{
id: "3",
name: "Carol White",
email: "carol@example.com",
role: "viewer",
status: "inactive",
},
]
// Define columns
export const columns: ColumnDef<User>[] = [
{
accessorKey: "name",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => <div className="capitalize">{row.getValue("name")}</div>,
},
{
accessorKey: "email",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => <div className="lowercase">{row.getValue("email")}</div>,
},
{
accessorKey: "role",
header: "Role",
cell: ({ row }) => (
<div className="capitalize">{row.getValue("role")}</div>
),
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => (
<div className="capitalize">{row.getValue("status")}</div>
),
},
{
id: "actions",
enableHiding: false,
cell: ({ row }) => {
const user = row.original
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(user.id)}
>
Copy user ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>View user</DropdownMenuItem>
<DropdownMenuItem>Edit user</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
},
},
]
export function DataTableExample() {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = React.useState({})
const [rowSelection, setRowSelection] = React.useState({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
})
return (
<div className="w-full">
<div className="flex items-center py-4">
<Input
placeholder="Filter names..."
value={(table.getColumn("name")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("name")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Columns <ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
)
}
/**
* Key Patterns Demonstrated:
*
* 1. TanStack Table Integration: Using @tanstack/react-table with shadcn/ui
* 2. Sorting: Click headers to sort ascending/descending
* 3. Filtering: Text input to filter table data
* 4. Column Visibility: Toggle columns via dropdown menu
* 5. Pagination: Built-in pagination controls
* 6. Row Actions: Dropdown menu per row for context actions
* 7. Responsive Design: Table adapts to different screen sizes
*
* Required Dependencies:
* - @tanstack/react-table
* - lucide-react
*
* Installation:
* npx shadcn@latest add table
* npx shadcn@latest add button
* npx shadcn@latest add input
* npx shadcn@latest add dropdown-menu
* npm install @tanstack/react-table lucide-react
*/
// Example: Form Pattern with shadcn/ui components
// Demonstrates: Form building, validation, and composition
"use client"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
import { toast } from "@/components/ui/use-toast"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
// Define form schema with zod
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
email: z.string().email({
message: "Please enter a valid email address.",
}),
role: z.enum(["admin", "user", "viewer"], {
required_error: "Please select a role.",
}),
bio: z.string().max(160, {
message: "Bio must not be longer than 160 characters.",
}).optional(),
})
type FormValues = z.infer<typeof formSchema>
export function UserProfileForm() {
// Initialize form with react-hook-form and zod validation
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
username: "",
email: "",
bio: "",
},
})
// Handle form submission
function onSubmit(values: FormValues) {
// In a real app, send to API
console.log(values)
toast({
title: "Profile updated",
description: "Your profile has been successfully updated.",
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="johndoe" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="john@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem>
<FormLabel>Role</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a role" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="user">User</SelectItem>
<SelectItem value="viewer">Viewer</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Your role determines your access level.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="Tell us about yourself"
className="resize-none"
{...field}
/>
</FormControl>
<FormDescription>
Optional. Maximum 160 characters.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Update profile</Button>
</form>
</Form>
)
}
/**
* Key Patterns Demonstrated:
*
* 1. Form Composition: Using shadcn/ui's Form components with react-hook-form
* 2. Validation: Zod schema for type-safe validation
* 3. Error Handling: Automatic error messages via FormMessage
* 4. Accessibility: All fields properly labeled with descriptions
* 5. Type Safety: TypeScript types inferred from Zod schema
*
* Required Dependencies:
* - react-hook-form
* - @hookform/resolvers
* - zod
*
* Installation:
* npx shadcn@latest add form
* npx shadcn@latest add input
* npx shadcn@latest add select
* npx shadcn@latest add textarea
* npx shadcn@latest add button
*/
shadcn/ui Integration Skill
Install
npx skills add google-labs-code/stitch-skills --skill shadcn-ui --globalWhat It Does
This skill provides expert guidance for integrating shadcn/ui components into your React applications. It helps you discover, install, customize, and optimize shadcn/ui components while following best practices.
Example Prompts
Help me set up shadcn/ui in my Next.js project
Add a data table component with sorting and filtering to my app
Show me how to customize the button component with a new variant
Create a login form using shadcn/ui components with validation
Build a dashboard layout with sidebar navigation using shadcn/ui blocksWhat is shadcn/ui?
shadcn/ui is a collection of beautifully designed, accessible, and customizable components built with:
- Radix UI or Base UI: Unstyled, accessible component primitives
- Tailwind CSS: Utility-first styling framework
- TypeScript: Full type safety
Key Difference: Unlike traditional component libraries, shadcn/ui copies components directly into your project. This gives you:
- Full control over the code
- No version lock-in
- Complete customization freedom
- Zero runtime overhead
Skill Structure
skills/shadcn-ui/
├── SKILL.md — Core instructions & workflow
├── README.md — This file
├── examples/ — Example implementations
│ ├── form-pattern.tsx — Form with validation
│ ├── data-table.tsx — Advanced table with sorting
│ └── auth-layout.tsx — Authentication flow
├── resources/ — Reference documentation
│ ├── setup-guide.md — Project initialization
│ ├── component-catalog.md — Component reference
│ ├── customization-guide.md — Theming patterns
│ └── migration-guide.md — Migration from other libraries
└── scripts/ — Utility scripts
└── verify-setup.sh — Validate project configurationHow It Works
When activated, the agent follows this workflow:
1. Discovery & Planning
- Lists available components using shadcn MCP tools
- Identifies required dependencies
- Plans component composition strategy
2. Setup & Configuration
- Verifies or initializes
components.json - Checks Tailwind CSS configuration
- Validates import aliases and paths
3. Component Integration
- Retrieves component source code
- Installs via CLI or manual integration
- Handles dependency installation
4. Customization
- Applies theme customization
- Creates component variants
- Extends components with custom logic
5. Quality Assurance
- Validates TypeScript types
- Checks accessibility compliance
- Verifies responsive behavior
Prerequisites
Your project should have:
- React 18+
- Tailwind CSS 3.0+
- TypeScript (recommended)
- Node.js 18+
Quick Start
For New Projects
# Create Next.js project with shadcn/ui
npx create-next-app@latest my-app
cd my-app
npx shadcn@latest init
# Add components
npx shadcn@latest add button
npx shadcn@latest add cardFor Existing Projects
# Initialize shadcn/ui
npx shadcn@latest init
# Configure when prompted:
# - Choose style (default/new-york)
# - Select base color
# - Configure CSS variables
# - Set import aliases
# Add your first component
npx shadcn@latest add buttonAvailable Components
shadcn/ui provides 50+ components including:
Layout: Accordion, Card, Separator, Tabs, Collapsible Forms: Button, Input, Label, Checkbox, Radio Group, Select, Textarea Data Display: Table, Badge, Avatar, Progress, Skeleton Overlays: Dialog, Sheet, Popover, Tooltip, Dropdown Menu Navigation: Navigation Menu, Tabs, Breadcrumb, Pagination Feedback: Alert, Alert Dialog, Toast, Command
Plus complete Blocks like:
- Authentication forms
- Dashboard layouts
- Calendar interfaces
- Sidebar navigation
- E-commerce components
Customization Approach
Theme-Level Customization
Modify CSS variables in globals.css:
:root {
--primary: 221.2 83.2% 53.3%;
--secondary: 210 40% 96.1%;
/* ... */
}Component-Level Customization
Components use class-variance-authority for variants:
const buttonVariants = cva(
"base-classes",
{
variants: {
variant: { default: "...", destructive: "..." },
size: { default: "...", sm: "..." },
}
}
)Composition
Create higher-level components:
// Compose existing components
export function FeatureCard({ title, description, icon }) {
return (
<Card>
<CardHeader>
{icon}
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent>
<p>{description}</p>
</CardContent>
</Card>
)
}Integration with MCP Tools
This skill leverages shadcn MCP server capabilities:
list_components- Browse component catalogget_component- Retrieve component sourceget_component_metadata- View props and dependenciesget_component_demo- See usage exampleslist_blocks- Browse UI blocksget_block- Retrieve block source with all filessearch_items_in_registries- Find components in custom registries
Best Practices
1. Keep `ui/` pure: Don't modify components in components/ui/ directly 2. Compose, don't fork: Create wrapper components instead of editing originals 3. Use the CLI: Let shadcn add handle dependencies and updates 4. Maintain consistency: Use the cn() utility for all class merging 5. Respect accessibility: Preserve ARIA attributes and keyboard handlers 6. Test responsiveness: shadcn components are responsive by default—keep it that way
Troubleshooting
"Module not found" errors
Check your tsconfig.json includes path aliases:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}Styles not applying
- Import
globals.cssin your root layout - Verify Tailwind config includes component paths
- Check CSS variable definitions match component expectations
TypeScript errors
- Ensure all Radix UI peer dependencies are installed
- Run
npm installafter adding components via CLI - Check that React types are up to date
Further Reading
Contributing
Contributions to improve this skill are welcome! See the root CONTRIBUTING.md for guidelines.
License
See LICENSE in the repository root.
shadcn/ui Component Catalog
Complete reference of all available shadcn/ui components, organized by category.
Note: This catalog lists dependencies based on Radix UI. If you are using Base UI, the add command will handle the correct dependencies automatically.Layout Components
Accordion
Collapsible content sections.
npx shadcn@latest add accordionUse cases: FAQs, settings panels, navigation menus Key props: type (single/multiple), collapsible Dependencies: @radix-ui/react-accordion
Card
Container for grouping related content.
npx shadcn@latest add cardSub-components: CardHeader, CardTitle, CardDescription, CardContent, CardFooter Use cases: Product cards, profile cards, dashboard widgets Dependencies: None
Separator
Visual divider between content sections.
npx shadcn@latest add separatorProps: orientation (horizontal/vertical), decorative Dependencies: @radix-ui/react-separator
Tabs
Organize content into multiple panels, one visible at a time.
npx shadcn@latest add tabsSub-components: TabsList, TabsTrigger, TabsContent Use cases: Settings pages, dashboards, multi-step forms Dependencies: @radix-ui/react-tabs
Collapsible
Show/hide content with smooth animation.
npx shadcn@latest add collapsibleProps: open, onOpenChange, disabled Dependencies: @radix-ui/react-collapsible
Form Components
Button
Clickable button with variants and sizes.
npx shadcn@latest add buttonVariants: default, destructive, outline, secondary, ghost, link Sizes: default, sm, lg, icon Dependencies: @radix-ui/react-slot
Input
Text input field.
npx shadcn@latest add inputTypes: text, email, password, number, tel, url Use cases: Forms, search bars, filters Dependencies: None
Label
Accessible label for form fields.
npx shadcn@latest add labelUse with: All form inputs for accessibility Dependencies: @radix-ui/react-label
Textarea
Multi-line text input.
npx shadcn@latest add textareaProps: rows, cols, resize (via className) Dependencies: None
Checkbox
Binary toggle control.
npx shadcn@latest add checkboxProps: checked, onCheckedChange, disabled Dependencies: @radix-ui/react-checkbox
Radio Group
Select one option from a set.
npx shadcn@latest add radio-groupSub-components: RadioGroupItem Dependencies: @radix-ui/react-radio-group
Select
Dropdown selection control.
npx shadcn@latest add selectSub-components: SelectTrigger, SelectContent, SelectItem, SelectValue Use cases: Country selectors, category filters Dependencies: @radix-ui/react-select
Switch
Binary toggle with visual feedback.
npx shadcn@latest add switchProps: checked, onCheckedChange, disabled Use cases: Settings toggles, feature flags Dependencies: @radix-ui/react-switch
Slider
Select value from a range.
npx shadcn@latest add sliderProps: min, max, step, value, onValueChange Use cases: Volume controls, filters, settings Dependencies: @radix-ui/react-slider
Form
Comprehensive form component with validation.
npx shadcn@latest add formSub-components: FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage Best used with: react-hook-form, zod Dependencies: @radix-ui/react-label, @radix-ui/react-slot
Data Display
Table
Display structured data in rows and columns.
npx shadcn@latest add tableSub-components: TableHeader, TableBody, TableHead, TableRow, TableCell, TableFooter, TableCaption Best used with: @tanstack/react-table Dependencies: None
Badge
Highlight status or category.
npx shadcn@latest add badgeVariants: default, secondary, destructive, outline Use cases: Status indicators, tags, notifications Dependencies: None
Avatar
Display user profile images with fallback.
npx shadcn@latest add avatarSub-components: AvatarImage, AvatarFallback Dependencies: @radix-ui/react-avatar
Progress
Visual indicator of task completion.
npx shadcn@latest add progressProps: value (0-100) Dependencies: @radix-ui/react-progress
Skeleton
Loading placeholder with animation.
npx shadcn@latest add skeletonUse cases: Content loading states Dependencies: None
Calendar
Date selection interface.
npx shadcn@latest add calendarProps: mode (single/multiple/range), selected, onSelect Dependencies: react-day-picker
Overlay Components
Dialog
Modal dialog overlay.
npx shadcn@latest add dialogSub-components: DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription Use cases: Confirmations, forms, detailed views Dependencies: @radix-ui/react-dialog
Sheet
Side panel that slides in from edge.
npx shadcn@latest add sheetSides: top, right, bottom, left Sub-components: SheetTrigger, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription Use cases: Navigation menus, filters, settings Dependencies: @radix-ui/react-dialog
Popover
Floating content container.
npx shadcn@latest add popoverSub-components: PopoverTrigger, PopoverContent Use cases: Tooltips with actions, mini forms Dependencies: @radix-ui/react-popover
Tooltip
Contextual information on hover.
npx shadcn@latest add tooltipSub-components: TooltipProvider, TooltipTrigger, TooltipContent Props: side, sideOffset, delayDuration Dependencies: @radix-ui/react-tooltip
Dropdown Menu
Context menu with actions.
npx shadcn@latest add dropdown-menuSub-components: DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuSeparator, DropdownMenuLabel Use cases: Action menus, settings Dependencies: @radix-ui/react-dropdown-menu
Context Menu
Right-click menu.
npx shadcn@latest add context-menuSub-components: Similar to DropdownMenu Use cases: Right-click actions, advanced UIs Dependencies: @radix-ui/react-context-menu
Menubar
Horizontal menu bar.
npx shadcn@latest add menubarSub-components: MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem Use cases: Application menus (File, Edit, View) Dependencies: @radix-ui/react-menubar
Alert Dialog
Modal dialog for important confirmations.
npx shadcn@latest add alert-dialogSub-components: AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel Use cases: Delete confirmations, destructive actions Dependencies: @radix-ui/react-alert-dialog
Hover Card
Content card revealed on hover.
npx shadcn@latest add hover-cardSub-components: HoverCardTrigger, HoverCardContent Use cases: User previews, detailed information Dependencies: @radix-ui/react-hover-card
Navigation
Navigation Menu
Accessible navigation with dropdowns.
npx shadcn@latest add navigation-menuSub-components: NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink Use cases: Main site navigation Dependencies: @radix-ui/react-navigation-menu
Breadcrumb
Show current page location in hierarchy.
npx shadcn@latest add breadcrumbSub-components: BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator Use cases: Multi-level navigation Dependencies: None
Pagination
Navigate through pages of content.
npx shadcn@latest add paginationSub-components: PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis Dependencies: None
Feedback Components
Alert
Display important messages.
npx shadcn@latest add alertVariants: default, destructive Sub-components: AlertTitle, AlertDescription Use cases: Error messages, warnings, info Dependencies: None
Toast
Temporary notification message.
npx shadcn@latest add toastProps: title, description, action, variant Usage: Via useToast() hook Dependencies: @radix-ui/react-toast
Sonner
Alternative toast implementation.
npx shadcn@latest add sonnerBetter for: Rich notifications, multiple toasts Dependencies: sonner
Command & Search
Command
Command palette with search and keyboard navigation.
npx shadcn@latest add commandSub-components: CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator Use cases: Command palettes, search interfaces Dependencies: cmdk
Combobox
Searchable select dropdown.
npx shadcn@latest add comboboxUse cases: Autocomplete, country selectors Dependencies: cmdk (via command)
Utility Components
Aspect Ratio
Maintain consistent width-height ratio.
npx shadcn@latest add aspect-ratioProps: ratio (e.g., 16/9, 4/3) Dependencies: @radix-ui/react-aspect-ratio
Scroll Area
Custom scrollbar styling.
npx shadcn@latest add scroll-areaUse cases: Custom scrollable areas Dependencies: @radix-ui/react-scroll-area
Resizable
Resizable panel layout.
npx shadcn@latest add resizableSub-components: ResizablePanelGroup, ResizablePanel, ResizableHandle Use cases: Split views, adjustable layouts Dependencies: react-resizable-panels
Date & Time
Date Picker
Select dates with calendar popup.
npx shadcn@latest add date-pickerVariants: Single date, date range Dependencies: react-day-picker, date-fns
Advanced Components
Carousel
Slideshow component.
npx shadcn@latest add carouselSub-components: CarouselContent, CarouselItem, CarouselPrevious, CarouselNext Dependencies: embla-carousel-react
Drawer
Bottom drawer for mobile interfaces.
npx shadcn@latest add drawerBest for: Mobile-first designs Dependencies: vaul
Component Composition Patterns
Form + Validation Pattern
npx shadcn@latest add form input label button
npm install react-hook-form zod @hookform/resolversData Table Pattern
npx shadcn@latest add table button dropdown-menu
npm install @tanstack/react-tableDashboard Layout Pattern
npx shadcn@latest add card tabs badge avatarAuthentication Pattern
npx shadcn@latest add card input label button tabsQuick Reference
| Component | Category | Complexity | Dependencies |
|---|---|---|---|
| Button | Form | Simple | radix-slot |
| Input | Form | Simple | None |
| Card | Layout | Simple | None |
| Dialog | Overlay | Medium | radix-dialog |
| Table | Data | Simple | None |
| Form | Form | Complex | radix-label/slot |
| Command | Search | Complex | cmdk |
| Calendar | Date | Medium | react-day-picker |
Installation Shortcuts
Common component bundles:
# Essential forms
npx shadcn@latest add form input label button select checkbox
# Data display
npx shadcn@latest add table badge avatar progress skeleton
# Overlay/modal components
npx shadcn@latest add dialog sheet popover tooltip alert-dialog
# Navigation
npx shadcn@latest add navigation-menu breadcrumb pagination
# Layout
npx shadcn@latest add card accordion tabs separatorComponent Updates
To update components to the latest version:
# Re-add component (will prompt to overwrite)
npx shadcn@latest add button
# Or use diff command to see changes
npx shadcn@latest diff buttonFurther Reading
shadcn/ui Customization Guide
Learn how to customize shadcn/ui components to match your brand and design requirements.
Theming Approach
shadcn/ui uses a CSS variable-based theming system, making it easy to customize colors, spacing, and other design tokens globally.
Color Customization
Understanding the Color System
shadcn/ui uses HSL color values stored as CSS variables. Each color has a base value and a foreground variant for text/content that appears on top of it.
Base Color Variables (in globals.css):
:root {
--background: 0 0% 100%; /* Page background */
--foreground: 222.2 84% 4.9%; /* Primary text color */
--primary: 221.2 83.2% 53.3%; /* Primary brand color */
--primary-foreground: 210 40% 98%; /* Text on primary */
--secondary: 210 40% 96.1%; /* Secondary actions */
--accent: 210 40% 96.1%; /* Accent highlights */
--muted: 210 40% 96.1%; /* Muted backgrounds */
--destructive: 0 84.2% 60.2%; /* Error/danger */
--border: 214.3 31.8% 91.4%; /* Border colors */
--input: 214.3 31.8% 91.4%; /* Input borders */
--ring: 221.2 83.2% 53.3%; /* Focus rings */
}Changing Brand Colors
To match your brand, update the primary color:
:root {
/* Original blue */
--primary: 221.2 83.2% 53.3%;
/* Change to brand purple */
--primary: 270 91% 65%;
/* Adjust foreground for contrast */
--primary-foreground: 0 0% 100%;
}HSL Format: hue saturation lightness
- Hue: 0-360 (color wheel position)
- Saturation: 0-100% (color intensity)
- Lightness: 0-100% (brightness)
Tools for Color Selection
1. HSL Color Picker: https://hslpicker.com/ 2. Shadcn Theme Generator: https://ui.shadcn.com/themes 3. Coolors: https://coolors.co/ (generates palettes)
Creating a Color Scheme
Start with your primary brand color, then derive other colors:
:root {
/* 1. Primary brand color */
--primary: 230 90% 60%;
--primary-foreground: 0 0% 100%;
/* 2. Lighter variant for secondary */
--secondary: 230 30% 95%;
--secondary-foreground: 230 90% 30%;
/* 3. Subtle accent (shift hue slightly) */
--accent: 200 90% 60%;
--accent-foreground: 0 0% 100%;
/* 4. Muted backgrounds (low saturation) */
--muted: 230 20% 96%;
--muted-foreground: 230 20% 40%;
/* 5. Keep destructive red-based */
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 100%;
}Dark Mode
Setting Up Dark Mode
shadcn/ui includes dark mode support out of the box. Add dark mode colors:
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
/* ... all color variables */
}Toggle Dark Mode
Next.js with next-themes:
npm install next-themes// app/providers.tsx
"use client"
import { ThemeProvider } from "next-themes"
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
)
}
// app/layout.tsx
import { Providers } from "./providers"
export default function RootLayout({ children }) {
return (
<html suppressHydrationWarning>
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}
// components/theme-toggle.tsx
"use client"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
export function ThemeToggle() {
const { setTheme, theme } = useTheme()
return (
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
)
}Component Customization
Using Variants
shadcn/ui components use class-variance-authority (cva) for variants. Example from Button:
// components/ui/button.tsx
import { cva, type VariantProps } from "class-variance-authority"
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)Adding Custom Variants
To add a new variant, edit the component file in components/ui/:
// Add new "success" variant to button
const buttonVariants = cva(
"...",
{
variants: {
variant: {
default: "...",
destructive: "...",
// Add new variant
success: "bg-green-600 text-white hover:bg-green-700",
},
// Add new size
size: {
default: "...",
xl: "h-12 rounded-md px-10 text-base",
},
},
}
)
// Update TypeScript interface
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}Usage:
<Button variant="success" size="xl">Save Changes</Button>Creating Composite Components
Don't modify components/ui/ directly. Instead, create wrapper components:
// components/loading-button.tsx
import { Button, ButtonProps } from "@/components/ui/button"
import { Loader2 } from "lucide-react"
interface LoadingButtonProps extends ButtonProps {
loading?: boolean
}
export function LoadingButton({
loading,
children,
disabled,
...props
}: LoadingButtonProps) {
return (
<Button disabled={loading || disabled} {...props}>
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{children}
</Button>
)
}Typography Customization
Font Family
Update tailwind.config.js:
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
heading: ['Poppins', 'system-ui', 'sans-serif'],
mono: ['Fira Code', 'monospace'],
},
},
},
}Import fonts in your layout:
// app/layout.tsx
import { Inter, Poppins } from 'next/font/google'
const inter = Inter({ subsets: ['latin'], variable: '--font-sans' })
const poppins = Poppins({
weight: ['600', '700'],
subsets: ['latin'],
variable: '--font-heading',
})
export default function RootLayout({ children }) {
return (
<html className={`${inter.variable} ${poppins.variable}`}>
<body className="font-sans">{children}</body>
</html>
)
}Use in components:
<h1 className="font-heading text-3xl">Heading</h1>
<p className="font-sans">Body text</p>Font Sizes
Extend Tailwind's font scale:
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontSize: {
'xs': '0.75rem', // 12px
'sm': '0.875rem', // 14px
'base': '1rem', // 16px
'lg': '1.125rem', // 18px
'xl': '1.25rem', // 20px
'2xl': '1.5rem', // 24px
'3xl': '1.875rem', // 30px
'4xl': '2.25rem', // 36px
'5xl': '3rem', // 48px
'6xl': '3.75rem', // 60px
'7xl': '4.5rem', // 72px
},
},
},
}Spacing & Layout
Border Radius
Customize roundedness globally:
/* globals.css */
:root {
--radius: 0.5rem; /* Default (8px) */
/* More rounded */
--radius: 1rem; /* 16px */
/* Sharp edges */
--radius: 0; /* No rounding */
/* Very rounded */
--radius: 1.5rem; /* 24px */
}This affects all components using rounded-lg, rounded-md, rounded-sm.
Custom Spacing
Extend Tailwind's spacing scale:
// tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: {
'72': '18rem',
'84': '21rem',
'96': '24rem',
},
},
},
}Animation Customization
Adjusting Existing Animations
// tailwind.config.js
module.exports = {
theme: {
extend: {
keyframes: {
// Make accordion faster
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.15s ease-out", // Faster
"accordion-up": "accordion-up 0.15s ease-out",
},
},
},
}Adding New Animations
// tailwind.config.js
module.exports = {
theme: {
extend: {
keyframes: {
"fade-in": {
"0%": { opacity: 0 },
"100%": { opacity: 1 },
},
"slide-in-bottom": {
"0%": { transform: "translateY(100%)" },
"100%": { transform: "translateY(0)" },
},
},
animation: {
"fade-in": "fade-in 0.3s ease-out",
"slide-in-bottom": "slide-in-bottom 0.3s ease-out",
},
},
},
}Use in components:
<div className="animate-fade-in">Content</div>Advanced Customization
Creating a Design System
Structure your customizations:
src/
├── styles/
│ ├── globals.css # CSS variables & base styles
│ ├── themes/
│ │ ├── default.css # Default theme
│ │ └── brand.css # Brand-specific overrides
│ └── components/
│ └── custom.css # Component-specific styles
├── lib/
│ └── design-tokens.ts # Shared constants
└── components/
├── ui/ # shadcn components (don't modify)
└── custom/ # Your wrapper componentsDesign Tokens File
// lib/design-tokens.ts
export const designTokens = {
colors: {
brand: {
primary: 'hsl(230, 90%, 60%)',
secondary: 'hsl(230, 30%, 95%)',
},
},
spacing: {
section: '5rem',
card: '1.5rem',
},
radius: {
card: '1rem',
button: '0.5rem',
},
typography: {
h1: 'text-5xl font-heading font-bold',
h2: 'text-4xl font-heading font-semibold',
h3: 'text-3xl font-heading font-semibold',
body: 'text-base font-sans',
small: 'text-sm text-muted-foreground',
},
} as constUse in components:
import { designTokens } from "@/lib/design-tokens"
<h1 className={designTokens.typography.h1}>Title</h1>Best Practices
1. Don't modify `components/ui/` files directly: Create wrapper components instead 2. Use CSS variables for theming: Easier to maintain and switch themes 3. Leverage Tailwind's `extend`: Don't replace defaults, extend them 4. Keep variants in component files: Co-locate variant logic with components 5. Test dark mode: Ensure all customizations work in both themes 6. Document custom variants: Add comments for custom additions 7. Use consistent spacing: Stick to Tailwind's spacing scale 8. Maintain accessibility: Don't sacrifice contrast for aesthetics
Resources
Migration Guide to shadcn/ui
This guide helps you migrate from other UI libraries to shadcn/ui.
Why Migrate to shadcn/ui?
- Full ownership: Code lives in your project, not node_modules
- Customizable: Modify any component to fit your needs
- No breaking changes: Update components individually
- Smaller bundles: Only include what you use
- Modern stack: Built with Radix UI and Tailwind CSS
- Type-safe: Full TypeScript support
Migration Strategies
Strategy 1: Incremental Migration (Recommended)
Gradually replace components over time:
1. Install shadcn/ui alongside existing library 2. Replace components page by page or feature by feature 3. Remove old library once migration is complete
Pros: Low risk, can be done alongside feature work Cons: Temporary bundle size increase
Strategy 2: Big Bang Migration
Replace all components at once:
1. Set up shadcn/ui 2. Create component mapping document 3. Replace all components in one effort 4. Test thoroughly
Pros: Clean cutover, no mixed UI Cons: High risk, requires dedicated time
Internal Migrations (shadcn specific)
RTL Support Migration
If you need to support RTL languages (like Arabic or Hebrew) in an existing shadcn/ui project:
npx shadcn@latest migrate rtlThis CLI command transforms your components to use logical properties:
ml-4->ms-4(margin-start)pl-4->ps-4(padding-start)text-left->text-start
It ensures your UI adapts correctly to layout direction without manual refactoring.
From Material-UI (MUI)
Component Mapping
| MUI Component | shadcn/ui Equivalent | Notes |
|---|---|---|
| Button | Button | Similar API |
| TextField | Input + Label | Separate components |
| Select | Select | Different structure |
| Dialog | Dialog | Similar concept |
| Drawer | Sheet | Side panel |
| Card | Card | Very similar |
| Table | Table | Use with TanStack Table |
| Checkbox | Checkbox | Similar API |
| Switch | Switch | Similar API |
| Tabs | Tabs | Similar structure |
| Tooltip | Tooltip | Simpler API |
| Menu | Dropdown Menu | Different trigger model |
| Snackbar | Toast | Different implementation |
| Autocomplete | Combobox | Use with Command |
Key Differences
1. Import Structure
// MUI
import Button from '@mui/material/Button'
// shadcn/ui
import { Button } from '@/components/ui/button'2. Form Components
// MUI
<TextField
label="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={!!errors.email}
helperText={errors.email}
/>
// shadcn/ui
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{errors.email && (
<p className="text-sm text-destructive">{errors.email}</p>
)}
</div>
// Or with Form component
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>3. Theming
// MUI
import { ThemeProvider, createTheme } from '@mui/material/styles'
const theme = createTheme({
palette: {
primary: { main: '#1976d2' },
},
})
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
// shadcn/ui
// Edit globals.css
:root {
--primary: 215 100% 50%;
}4. Styling Approach
// MUI (sx prop)
<Button sx={{ px: 4, py: 2, borderRadius: 2 }}>
Click me
</Button>
// shadcn/ui (Tailwind classes)
<Button className="px-4 py-2 rounded-lg">
Click me
</Button>Migration Example: Login Form
Before (MUI):
import { TextField, Button, Box } from '@mui/material'
export function LoginForm() {
return (
<Box component="form" sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<TextField label="Email" type="email" required />
<TextField label="Password" type="password" required />
<Button variant="contained" type="submit">
Sign In
</Button>
</Box>
)
}After (shadcn/ui):
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Button } from '@/components/ui/button'
export function LoginForm() {
return (
<form className="flex flex-col gap-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" required />
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" required />
</div>
<Button type="submit">Sign In</Button>
</form>
)
}From Chakra UI
Component Mapping
| Chakra UI | shadcn/ui | Notes |
|---|---|---|
| Button | Button | Similar variants |
| Input | Input | More basic |
| Select | Select | Different structure |
| Modal | Dialog | Similar concept |
| Drawer | Sheet | Very similar |
| Box | div | Use Tailwind classes |
| Flex | div | Use flex utilities |
| Stack | div | Use space-y-* classes |
| Text | p/span | Use typography classes |
| Heading | h1/h2/etc | Use typography classes |
| useToast | useToast | Different API |
| Menu | Dropdown Menu | Similar |
Key Differences
1. Layout Components
// Chakra UI
<Stack spacing={4} direction="column">
<Box>Item 1</Box>
<Box>Item 2</Box>
</Stack>
// shadcn/ui
<div className="flex flex-col space-y-4">
<div>Item 1</div>
<div>Item 2</div>
</div>2. Responsive Styles
// Chakra UI
<Box display={{ base: 'block', md: 'flex' }} />
// shadcn/ui
<div className="block md:flex" />3. Color Mode
// Chakra UI
import { useColorMode } from '@chakra-ui/react'
const { colorMode, toggleColorMode } = useColorMode()
// shadcn/ui (with next-themes)
import { useTheme } from 'next-themes'
const { theme, setTheme } = useTheme()From Ant Design
Component Mapping
| Ant Design | shadcn/ui | Notes |
|---|---|---|
| Button | Button | Similar |
| Input | Input | More basic |
| Form | Form | Different approach |
| Table | Table | Use TanStack Table |
| Modal | Dialog | Similar |
| Drawer | Sheet | Similar |
| Select | Select | Different API |
| DatePicker | Calendar + Popover | More manual |
| Menu | Navigation Menu | Different |
| message | Toast | Different API |
| notification | Toast | Similar concept |
Key Differences
1. Form Handling
// Ant Design
<Form
form={form}
onFinish={onSubmit}
>
<Form.Item name="email" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">Submit</Button>
</Form.Item>
</Form>
// shadcn/ui (with react-hook-form)
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>2. Notifications
// Ant Design
import { message } from 'antd'
message.success('Success!')
// shadcn/ui
import { useToast } from '@/components/ui/use-toast'
const { toast } = useToast()
toast({
title: "Success!",
description: "Operation completed.",
})From Bootstrap
Component Mapping
| Bootstrap | shadcn/ui | Notes |
|---|---|---|
| btn | Button | Similar variants |
| form-control | Input | Similar |
| card | Card | Very similar structure |
| modal | Dialog | Different API |
| dropdown | Dropdown Menu | Similar concept |
| nav/navbar | Navigation Menu | Different |
| alert | Alert | Similar |
| badge | Badge | Similar |
| table | Table | Use with TanStack Table |
Key Differences
1. Class-Based vs Component-Based
// Bootstrap
<button className="btn btn-primary btn-lg">
Click me
</button>
// shadcn/ui
<Button variant="default" size="lg">
Click me
</Button>2. Cards
<!-- Bootstrap -->
<div class="card">
<div class="card-header">Title</div>
<div class="card-body">Content</div>
<div class="card-footer">Footer</div>
</div>
<!-- shadcn/ui -->
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
</CardHeader>
<CardContent>Content</CardContent>
<CardFooter>Footer</CardFooter>
</Card>Migration Checklist
Before Migration
- [ ] Audit current component usage
- [ ] Set up shadcn/ui in a test branch
- [ ] Create component mapping document
- [ ] Plan migration order (start with simple components)
- [ ] Set up Tailwind CSS properly
- [ ] Configure path aliases
During Migration
- [ ] Install shadcn/ui components as needed
- [ ] Replace components incrementally
- [ ] Update styling to use Tailwind classes
- [ ] Test each page/feature after migration
- [ ] Update tests to match new components
- [ ] Handle form validation (switch to react-hook-form + zod)
- [ ] Migrate theme/color variables
After Migration
- [ ] Remove old UI library dependencies
- [ ] Clean up unused imports
- [ ] Optimize bundle size
- [ ] Update documentation
- [ ] Review and update design system
- [ ] Train team on new components
Common Pitfalls
1. Not Setting Up Tailwind Properly
shadcn/ui requires Tailwind. Ensure:
tailwind.config.jsincludes correct content paths- CSS variables are defined in
globals.css - Tailwind plugins are installed (e.g.,
tailwindcss-animate)
2. Forgetting Path Aliases
Components use @/ imports. Configure:
tsconfig.jsonwith path aliases- Vite/Next.js config for runtime resolution
3. Trying to Match Old Library Exactly
Don't force shadcn/ui to work like your old library. Embrace the new patterns:
- Use composition over configuration
- Leverage Tailwind utilities
- Create wrapper components for custom needs
4. Not Using Form Libraries
shadcn/ui form components are basic. For complex forms, use:
react-hook-formfor form statezodfor validation- shadcn's
Formcomponent for integration
5. Ignoring Accessibility
While shadcn/ui is accessible by default, custom modifications can break this. Test with:
- Keyboard navigation
- Screen readers
- ARIA attribute validation
Getting Help
- Discord: shadcn/ui Discord
- GitHub Discussions: shadcn/ui Discussions
- Documentation: ui.shadcn.com
Next Steps
After migration: 1. Review the Customization Guide 2. Explore the Component Catalog 3. Check out Examples 4. Consider building a component library on top of shadcn/ui
shadcn/ui Setup Guide
This guide walks you through setting up shadcn/ui in both new and existing projects.
Prerequisites
Before you begin, ensure you have:
- Node.js 18+ installed
- React 18+ in your project
- Tailwind CSS 3.0+ configured
- A package manager: npm, yarn, pnpm, or bun
Quick Start (New Project)
Option 1: npx shadcn create (Recommended)
The easiest way to start a new project with shadcn/ui is using the create command, which allows you to customize everything (framework, library, style, font, etc.).
npx shadcn@latest createThis interactive command will guide you through: 1. Project Name: Directory for your app. 2. Visual Style: Choose from Vega, Nova, Maia, Lyra, Mira, or Classic. 3. Base Color: Select your primary theme color. 4. Framework: Next.js, Vite, Laravel, etc. 5. Component Library: Choose Radix UI or Base UI. 6. RTL Support: Enable Right-to-Left support if needed.
Option 2: Classic Manual Setup (Next.js)
# Create a new Next.js project
npx create-next-app@latest my-app
cd my-app
# Initialize shadcn/ui
npx shadcn@latest init
# Add your first component
npx shadcn@latest add buttonOption 3: Classic Manual Setup (Vite + React)
# Create a new Vite project
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
# Install Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# Initialize shadcn/ui
npx shadcn@latest init
# Add your first component
npx shadcn@latest add buttonExisting Project Setup
Step 1: Ensure Tailwind CSS is Installed
If Tailwind is not installed:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -pConfigure tailwind.config.js:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}Step 2: Initialize shadcn/ui
Run the initialization command:
npx shadcn@latest initYou'll be asked to configure:
1. Style: Choose between default, new-york, or one of the new styles (Vega, Nova, etc.)
default: Clean and minimal designnew-york: More refined with subtle details
2. Base Color: Choose your primary color palette
- slate, gray, zinc, neutral, or stone
3. CSS Variables: Recommend yes for easier theming
4. TypeScript: Recommend yes for type safety
5. Import Alias: Default is @/components (recommended)
6. React Server Components: Choose based on your framework
yesfor Next.js 13+ with app directorynofor Vite, CRA, or Next.js pages directory
7. RTL Support: Answer yes if you need Right-to-Left layout support (this will use logical properties like ms-4 instead of ml-4).
Advanced Features
Visual Styles
shadcn/ui now offers multiple visual styles beyond the defaults:
- Vega: The classic shadcn/ui look.
- Nova: Reduced padding/margins, compact.
- Maia: Soft, rounded, generous spacing.
- Lyra: Boxy, sharp, mono font friendly.
- Mira: Dense, compact.
Base UI Support
You can now choose between Radix UI and Base UI as the underlying primitive library. They share the same component API/abstraction, so your usage remains consistent.
Step 3: Verify Configuration
The init command creates/updates several files:
components.json (root of project):
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib"
}
}src/lib/utils.ts:
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}Updated tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
// ... more colors
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}Updated globals.css (or equivalent):
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... dark mode variables */
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}Step 4: Configure Path Aliases
Ensure your tsconfig.json includes path aliases:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}For Vite, also update vite.config.ts:
import path from "path"
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})Step 5: Add Components
Now you can add components:
# Add individual components
npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add dialog
# Add multiple components at once
npx shadcn@latest add button card dialog input labelComponents will be added to src/components/ui/ by default.
Framework-Specific Considerations
Next.js App Router
- Set
rsc: trueincomponents.json - Add
"use client"to stateful components - Import components work from server components by default
Next.js Pages Router
- Set
rsc: falseincomponents.json - All components are client-side by default
Vite
- Ensure path aliases are configured in
vite.config.ts - Import
globals.cssin your main entry point (main.tsx)
Create React App
- Use
cracoorreact-app-rewiredfor path alias support - Or use relative imports instead of aliases
Verification Steps
1. Check file structure:
src/
├── components/
│ └── ui/
├── lib/
│ └── utils.ts
└── index.css (or globals.css)2. Test a simple component:
import { Button } from "@/components/ui/button"
export default function App() {
return <Button>Click me</Button>
}3. Verify Tailwind is working:
- Component should render with proper styling
- Check browser dev tools for applied classes
4. Test dark mode (if using CSS variables):
<html className="dark">Troubleshooting
"Cannot find module '@/components/ui/button'"
Solution: Check path alias configuration in tsconfig.json and framework config.
Styles not applying
Solution:
- Ensure
globals.cssis imported in your app entry point - Verify Tailwind config
contentpaths include your files - Check CSS variables are defined in
globals.css
TypeScript errors in components
Solution:
- Run
npm installto ensure all dependencies are installed - Check that
@types/reactis installed - Restart TypeScript server in your editor
Components look broken
Solution:
- Verify
tailwindcss-animateis installed:npm install tailwindcss-animate - Check that CSS variables are properly defined
- Ensure you're not overriding component styles globally
Next Steps
1. Browse the component catalog 2. Read the customization guide 3. Check out example implementations in /examples 4. Join the shadcn/ui Discord
Additional Resources
#!/usr/bin/env bash
# shadcn/ui Setup Verification Script
# Validates that a project is correctly configured for shadcn/ui
set -e
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo "🔍 Verifying shadcn/ui setup..."
echo ""
# Check if components.json exists
if [ -f "components.json" ]; then
echo -e "${GREEN}✓${NC} components.json found"
else
echo -e "${RED}✗${NC} components.json not found"
echo -e " ${YELLOW}Run:${NC} npx shadcn@latest init"
exit 1
fi
# Check if tailwind.config exists
if [ -f "tailwind.config.js" ] || [ -f "tailwind.config.ts" ]; then
echo -e "${GREEN}✓${NC} Tailwind config found"
else
echo -e "${RED}✗${NC} tailwind.config.js not found"
echo -e " ${YELLOW}Install Tailwind:${NC} npm install -D tailwindcss postcss autoprefixer"
exit 1
fi
# Check if tsconfig.json has path aliases
if [ -f "tsconfig.json" ]; then
if grep -q '"@/\*"' tsconfig.json; then
echo -e "${GREEN}✓${NC} Path aliases configured in tsconfig.json"
else
echo -e "${YELLOW}⚠${NC} Path aliases not found in tsconfig.json"
echo " Add to compilerOptions.paths:"
echo ' "@/*": ["./src/*"]'
fi
else
echo -e "${YELLOW}⚠${NC} tsconfig.json not found (TypeScript not configured)"
fi
# Check if globals.css or equivalent exists
if [ -f "src/index.css" ] || [ -f "src/globals.css" ] || [ -f "app/globals.css" ]; then
echo -e "${GREEN}✓${NC} Global CSS file found"
# Check for Tailwind directives
CSS_FILE=$(find . -name "globals.css" -o -name "index.css" | head -n 1)
if grep -q "@tailwind base" "$CSS_FILE"; then
echo -e "${GREEN}✓${NC} Tailwind directives present"
else
echo -e "${RED}✗${NC} Tailwind directives missing"
echo " Add to your CSS file:"
echo " @tailwind base;"
echo " @tailwind components;"
echo " @tailwind utilities;"
fi
# Check for CSS variables
if grep -q "^:root" "$CSS_FILE" || grep -q "@layer base" "$CSS_FILE"; then
echo -e "${GREEN}✓${NC} CSS variables defined"
else
echo -e "${YELLOW}⚠${NC} CSS variables not found"
echo " shadcn/ui requires CSS variables for theming"
fi
else
echo -e "${RED}✗${NC} Global CSS file not found"
fi
# Check if components/ui directory exists
if [ -d "src/components/ui" ] || [ -d "components/ui" ]; then
echo -e "${GREEN}✓${NC} components/ui directory exists"
# Count components
COMPONENT_COUNT=$(find . -path "*/components/ui/*.tsx" -o -path "*/components/ui/*.jsx" | wc -l)
echo -e " ${COMPONENT_COUNT} components installed"
else
echo -e "${YELLOW}⚠${NC} components/ui directory not found"
echo " Add your first component: npx shadcn@latest add button"
fi
# Check if lib/utils exists
if [ -f "src/lib/utils.ts" ] || [ -f "lib/utils.ts" ]; then
echo -e "${GREEN}✓${NC} lib/utils.ts exists"
# Check for cn function
UTILS_FILE=$(find . -name "utils.ts" | grep "lib" | head -n 1)
if grep -q "export function cn" "$UTILS_FILE"; then
echo -e "${GREEN}✓${NC} cn() utility function present"
else
echo -e "${RED}✗${NC} cn() utility function missing"
fi
else
echo -e "${RED}✗${NC} lib/utils.ts not found"
fi
# Check package.json dependencies
if [ -f "package.json" ]; then
echo ""
echo "📦 Checking dependencies..."
# Required dependencies
REQUIRED_DEPS=("react" "tailwindcss")
RECOMMENDED_DEPS=("class-variance-authority" "clsx" "tailwind-merge" "tailwindcss-animate")
for dep in "${REQUIRED_DEPS[@]}"; do
if grep -q "\"$dep\"" package.json; then
echo -e "${GREEN}✓${NC} $dep installed"
else
echo -e "${RED}✗${NC} $dep not installed"
fi
done
echo ""
echo "Recommended dependencies:"
for dep in "${RECOMMENDED_DEPS[@]}"; do
if grep -q "\"$dep\"" package.json; then
echo -e "${GREEN}✓${NC} $dep installed"
else
echo -e "${YELLOW}⚠${NC} $dep not installed (recommended)"
fi
done
fi
echo ""
echo -e "${GREEN}✓${NC} Setup verification complete!"
echo ""
echo "Next steps:"
echo " 1. Add components: npx shadcn@latest add [component]"
echo " 2. View catalog: npx shadcn@latest add --help"
echo " 3. Browse docs: https://ui.shadcn.com"
Related skills
Forks & variants (2)
Shadcn Ui has 2 known copies in the catalog totaling 55 installs. They canonicalize to this original listing.
- leonxlnx - 54 installs
- csc3213-2026-group-b - 1 installs
How it compares
Pick shadcn-ui for owned Tailwind component scaffolding instead of opaque npm UI kits you cannot edit in-repo.
FAQ
Is shadcn/ui a traditional npm component library?
shadcn/ui is not a traditional component library; the shadcn-ui skill treats it as copy-into-repo components built with Radix UI or Base UI and Tailwind CSS so developers own and customize every source file.
Which tools can the shadcn-ui skill use?
The shadcn-ui skill allows shadcn and mcp_shadcn tools plus Read, Write, Bash, and web_fetch so agents can discover components, install files, and apply Tailwind customization in the project repository.
Is Shadcn Ui safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.