"Installs, configures, and implements shadcn/ui accessible React components. Use when setting up shadcn/ui, adding components, building forms with React Hook Form and Zod, customising themes, or implementing UI patterns like buttons, dialogs, tables, and data displays."
Resources
1Install
npx skillscat add costa-marcello/skillkit/shadcn-ui Install via the SkillsCat registry.
shadcn/ui Component Guide
Build accessible, customisable UI components with shadcn/ui, Radix UI, and Tailwind CSS.
When to Use
- Setting up a new project with shadcn/ui
- Installing or configuring components
- Building forms with React Hook Form and Zod
- Creating accessible UI (buttons, dialogs, dropdowns, sheets)
- Customising themes with Tailwind CSS variables
- Implementing design systems
MCP Tools (Preferred Workflow)
Use the shadcn MCP tools as the primary method for component discovery and installation. Fall back to CLI commands only when MCP tools are unavailable.
Discovery Workflow
- Check project registries: Call
mcp__shadcn__get_project_registriesto verifycomponents.jsonexists - Search for components: Call
mcp__shadcn__search_items_in_registrieswith the registry and query - View component details: Call
mcp__shadcn__view_items_in_registriesto see source code and dependencies - Get usage examples: Call
mcp__shadcn__get_item_examples_from_registriesfor implementation patterns - Get install command: Call
mcp__shadcn__get_add_command_for_itemsfor the exact CLI command
MCP Tool Reference
| Tool | Purpose |
|---|---|
get_project_registries |
Check configured registries from components.json |
search_items_in_registries |
Fuzzy search for components by name/description |
list_items_in_registries |
List all available components in a registry |
view_items_in_registries |
View source code, types, and dependencies |
get_item_examples_from_registries |
Get usage examples for components |
get_add_command_for_items |
Get the CLI install command for components |
get_audit_checklist |
Get accessibility and quality audit checklist |
- Search:
search_items_in_registries(registries: ["@shadcn"], query: "date picker") - View:
view_items_in_registries(items: ["@shadcn/date-picker"]) - Examples:
get_item_examples_from_registries(items: ["@shadcn/date-picker"]) - Install:
get_add_command_for_items(items: ["@shadcn/date-picker"])
CLI Commands
Quick Start
New project:
npx create-next-app@latest my-app --typescript --tailwind --eslint --app
cd my-app
npx shadcn@latest init
npx shadcn@latest add button input form card dialog selectExisting project:
npx shadcn@latest initCLI Reference
| Command | Purpose |
|---|---|
npx shadcn@latest init |
Initialise shadcn/ui in a project |
npx shadcn@latest add <component> |
Add a component |
npx shadcn@latest add --all |
Add all components |
npx shadcn@latest search <query> |
Search registries |
npx shadcn@latest list |
List available components |
npx shadcn@latest view <component> |
View a component before installing |
npx shadcn@latest build |
Generate registry JSON files |
npx shadcn@latest migrate |
Run project migrations |
See references/cli-reference.md for framework-specific installation and registry configuration.
Setup Verification Checklist
After initialisation, verify the setup works:
Setup Verification:
- [ ] `components.json` exists in project root
- [ ] `@/components/ui/` directory created
- [ ] `@/lib/utils.ts` contains the `cn` function
- [ ] `globals.css` has CSS variables for theming
- [ ] `tailwind.config` includes shadcn colour tokens
- [ ] Test: `npx shadcn@latest add button` installs without errors
- [ ] Test: Import and render `<Button>` compiles without errorsComponent Quick Reference
| Component | Install | Key Props |
|---|---|---|
| Button | add button |
variant, size, asChild |
| Input | add input |
type, placeholder, disabled |
| Form | add form |
React Hook Form + Zod integration |
| Card | add card |
CardHeader, CardContent, CardFooter |
| Dialog | add dialog |
DialogTrigger, DialogContent |
| Select | add select |
SelectTrigger, SelectContent, SelectItem |
| Sheet | add sheet |
side="left|right|top|bottom" |
| Toast | add toast |
useToast() hook, variant |
| Table | add table |
TableHeader, TableBody, TableRow |
Install all: npx shadcn@latest add --all
Core Patterns
Button Variants
import { Button } from "@/components/ui/button"
// Variants: default, destructive, outline, secondary, ghost, link
<Button variant="destructive">Delete</Button>
// Sizes: default, sm, lg, icon
<Button size="icon"><Trash className="h-4 w-4" /></Button>
// Loading state
<Button disabled>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Loading
</Button>Form with Validation
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
import { Button } from "@/components/ui/button"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
export function LoginForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { email: "", password: "" },
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
<FormField control={form.control} name="email" render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl><Input type="email" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<FormField control={form.control} name="password" render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl><Input type="password" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<Button type="submit">Login</Button>
</form>
</Form>
)
}Dialog (Modal)
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Open</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>Make changes here.</DialogDescription>
</DialogHeader>
<div className="py-4">{/* Content */}</div>
<DialogFooter>
<Button type="submit">Save</Button>
</DialogFooter>
</DialogContent>
</Dialog>Toast Notifications
Add <Toaster /> to the root layout once (see references/nextjs-integration.md), then call useToast anywhere:
import { useToast } from "@/components/ui/use-toast"
const { toast } = useToast()
toast({ title: "Success", description: "Changes saved." })
toast({ variant: "destructive", title: "Error", description: "Something went wrong." })Theming
Customise via CSS variables in globals.css:
:root {
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--radius: 0.5rem;
}
.dark {
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
}See references/configuration.md for complete config files (TSConfig, Tailwind, CSS variables, dependencies).
Best Practices
- Verify accessibility: Run
get_audit_checklistafter adding components. Check keyboard navigation and screen reader output. - Validate with Zod: Define a Zod schema for every form. Pass it to
zodResolverinuseForm. - Use
cn()for all class merging: Never concatenate class strings manually.cn()resolves Tailwind conflicts. - Keep server components default: Only add
"use client"when the component uses hooks, event handlers, or browser APIs. Seereferences/nextjs-integration.md. - Theme with CSS variables: Change colours in
globals.css:rootand.darkblocks. Do not hardcode colour values in components.
async function onSubmit(values: z.infer<typeof formSchema>) {
try {
const res = await fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(values),
})
if (!res.ok) {
const data = await res.json()
toast({ variant: "destructive", title: "Submission failed", description: data.message ?? "Check your input and try again." })
return
}
toast({ title: "Saved", description: "Your changes have been saved." })
form.reset()
} catch {
toast({ variant: "destructive", title: "Network error", description: "Could not reach the server. Try again later." })
}
}
References
All detailed documentation is in references/:
| File | Content |
|---|---|
configuration.md |
TSConfig, Tailwind config, CSS variables, dependencies |
nextjs-integration.md |
App Router, Server Components, API routes |
advanced-patterns.md |
Complex forms, custom variants, dialog with forms, sheet nav |
cli-reference.md |
All CLI commands, framework installs, registries |
extended-components.md |
Terminal, Dock, Credit Card, QR Code, charts, animations, hooks |
learning-guide.md |
Learning path, exercises, CVA and cn patterns |
External Links:
- Official Docs: https://ui.shadcn.com
- Radix UI: https://www.radix-ui.com
- React Hook Form: https://react-hook-form.com
- Zod: https://zod.dev