Component Dependencies & Setup Checklist
Quick reference for what each shadcn component requires to work correctly.
Installation Commands
# Install a single component
npx shadcn@latest add button
# Install multiple components
npx shadcn@latest add button card dialog
# Install all components (use sparingly)
npx shadcn@latest add --allComponent Requirements Matrix
Basic Components (No Special Dependencies)
| Component | Install | Providers | Peer Deps |
|---|---|---|---|
| Button | npx shadcn add button |
None | None |
| Card | npx shadcn add card |
None | None |
| Badge | npx shadcn add badge |
None | None |
| Input | npx shadcn add input |
None | None |
| Label | npx shadcn add label |
None | None |
| Separator | npx shadcn add separator |
None | None |
| Skeleton | npx shadcn add skeleton |
None | None |
| Avatar | npx shadcn add avatar |
None | None |
| Aspect Ratio | npx shadcn add aspect-ratio |
None | None |
Components Requiring Providers
| Component | Install | Provider Required | Setup |
|---|---|---|---|
| Tooltip | npx shadcn add tooltip |
TooltipProvider |
Wrap app in layout |
| Theme Toggle | npx shadcn add + manual |
ThemeProvider |
Install next-themes |
| Sonner (Toast) | npx shadcn add sonner |
<Toaster /> |
Add to layout |
TooltipProvider Setup:
// app/layout.tsx
import { TooltipProvider } from "@/components/ui/tooltip"
export default function RootLayout({ children }) {
return (
<html>
<body>
<TooltipProvider>
{children}
</TooltipProvider>
</body>
</html>
)
}ThemeProvider Setup:
npm install next-themes// components/theme-provider.tsx
"use client"
import { ThemeProvider as NextThemesProvider } from "next-themes"
export function ThemeProvider({ children, ...props }) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
// app/layout.tsx
import { ThemeProvider } from "@/components/theme-provider"
export default function RootLayout({ children }) {
return (
<html suppressHydrationWarning>
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
>
{children}
</ThemeProvider>
</body>
</html>
)
}Sonner Setup:
// app/layout.tsx
import { Toaster } from "@/components/ui/sonner"
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Toaster />
</body>
</html>
)
}
// Usage anywhere:
import { toast } from "sonner"
toast("Event created")
toast.success("Success!")
toast.error("Error occurred")Components Requiring Peer Dependencies
| Component | Install | Peer Dependencies | Install Command |
|---|---|---|---|
| Form | npx shadcn add form |
react-hook-form, zod | npm i react-hook-form @hookform/resolvers zod |
| Data Table | npx shadcn add table |
@tanstack/react-table | npm i @tanstack/react-table |
| Charts | npx shadcn add chart |
recharts | npm i recharts |
| Calendar | npx shadcn add calendar |
react-day-picker, date-fns | npm i react-day-picker date-fns |
| Date Picker | npx shadcn add calendar popover button |
react-day-picker, date-fns | npm i react-day-picker date-fns |
| Carousel | npx shadcn add carousel |
embla-carousel-react | npm i embla-carousel-react |
| Drawer | npx shadcn add drawer |
vaul | npm i vaul |
Compound Component Structures
Components that require specific nesting to work:
Dialog
<Dialog>
<DialogTrigger asChild>
<Button>Open</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Title</DialogTitle> {/* Required for a11y */}
<DialogDescription>Description</DialogDescription>
</DialogHeader>
{/* Content */}
<DialogFooter>
<Button>Action</Button>
</DialogFooter>
</DialogContent>
</Dialog>Required subcomponents: DialogTrigger, DialogContent, DialogTitle
AlertDialog
<AlertDialog>
<AlertDialogTrigger asChild>
<Button>Delete</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>Required subcomponents: AlertDialogTrigger, AlertDialogContent, AlertDialogTitle, AlertDialogAction, AlertDialogCancel
Sheet
<Sheet>
<SheetTrigger asChild>
<Button>Open</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Title</SheetTitle> {/* Required for a11y */}
<SheetDescription>Description</SheetDescription>
</SheetHeader>
{/* Content */}
<SheetFooter>
<Button>Save</Button>
</SheetFooter>
</SheetContent>
</Sheet>Required subcomponents: SheetTrigger, SheetContent, SheetTitle
DropdownMenu
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button>Menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Required subcomponents: DropdownMenuTrigger, DropdownMenuContent
Select
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
<SelectItem value="option3">Option 3</SelectItem>
</SelectContent>
</Select>Required subcomponents: SelectTrigger, SelectValue, SelectContent, SelectItem
Tabs
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content 1</TabsContent>
<TabsContent value="tab2">Content 2</TabsContent>
</Tabs>Required subcomponents: TabsList, TabsTrigger, TabsContent
Accordion
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Section 1</AccordionTrigger>
<AccordionContent>Content 1</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Section 2</AccordionTrigger>
<AccordionContent>Content 2</AccordionContent>
</AccordionItem>
</Accordion>Required subcomponents: AccordionItem, AccordionTrigger, AccordionContent
Popover
<Popover>
<PopoverTrigger asChild>
<Button>Open</Button>
</PopoverTrigger>
<PopoverContent>
{/* Popover content */}
</PopoverContent>
</Popover>Required subcomponents: PopoverTrigger, PopoverContent
Command (Command Palette)
<Command>
<CommandInput placeholder="Search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>Calendar</CommandItem>
<CommandItem>Search</CommandItem>
</CommandGroup>
</CommandList>
</Command>Required subcomponents: CommandInput, CommandList
Sidebar (shadcn sidebar component)
<SidebarProvider>
<Sidebar>
<SidebarHeader>...</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Menu</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild>
<a href="/">Home</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>...</SidebarFooter>
</Sidebar>
<main>{children}</main>
</SidebarProvider>Required: Must be wrapped in SidebarProvider
Form Component Setup
Full form setup with react-hook-form and zod:
npm i react-hook-form @hookform/resolvers zod
npx shadcn add form input button"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
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.",
}),
})
export function ProfileForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
username: "",
email: "",
},
})
function onSubmit(values: z.infer<typeof formSchema>) {
console.log(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="johndoe" {...field} />
</FormControl>
<FormDescription>
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>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}Data Table Setup
Full data table with TanStack Table:
npm i @tanstack/react-table
npx shadcn add table"use client"
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
getPaginationRowModel,
getSortedRowModel,
SortingState,
} from "@tanstack/react-table"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { Button } from "@/components/ui/button"
import { useState } from "react"
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([])
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
state: {
sorting,
},
})
return (
<div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<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}>
{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">
<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>
)
}Common Import Patterns
Based on typical components.json setup:
// UI Components
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
// Form Components
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
// Dialog/Modal
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
// Utility
import { cn } from "@/lib/utils"Checking What's Installed
# List installed components
ls -la components/ui/
# Check components.json for config
cat components.jsoncomponents.json structure:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}