Pricing
✓ Pros
- ✓Copy source code into your project — no dependency lock-in
- ✓Accessible by default (Radix UI primitives, WAI-ARIA compliant)
- ✓Dark mode built-in with CSS variables
- ✓CLI: `npx shadcn@latest add button` installs directly into your project
- ✓Tailwind CSS — easy to customize to match your brand
- ✓TypeScript first, type-safe props
✗ Cons
- ✗Not a traditional component library — there's no package to update
- ✗Requires Tailwind CSS to be set up first
- ✗Some complex components require understanding Radix UI for deep customization
What is shadcn/ui?
shadcn/ui was created by shadcn (Shadid Haque, engineer at Vercel) and quickly became the most popular component collection in the React/Next.js ecosystem.
The philosophy is fundamentally different: you own the code. Instead of import { Button } from 'some-library', you copy the button component into components/ui/button.tsx and modify it however you like.
Getting started with Next.js
💻bash# Initialize shadcn in a Next.js project npx shadcn@latest init # Add the components you need npx shadcn@latest add button npx shadcn@latest add card npx shadcn@latest add dialog npx shadcn@latest add dropdown-menu npx shadcn@latest add form
The CLI automatically:
- Creates component files in
components/ui/ - Installs the required Radix UI dependencies
- Sets up CSS variables if they aren't already configured
Example component
💻tsximport { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; export function PricingCard() { return ( <Card className="w-[350px]"> <CardHeader> <CardTitle>Pro Plan</CardTitle> <CardDescription>$20/month</CardDescription> </CardHeader> <CardContent> <Button className="w-full">Get started</Button> </CardContent> </Card> ); }
Why is "copy-paste" better than a package?
Problems with traditional component libraries:
- Update the library → breaking changes → fix the entire codebase
- Want to customize → must override CSS, hack TypeScript types
- Bundle size grows even if you only use 5 components
The shadcn/ui approach:
- Code lives in your repo → you have full control
- Need a new prop? Edit the component file directly
- Updating? Re-run the CLI, review the diff, and merge manually
- Zero dependencies beyond Radix UI and Tailwind (both of which you already have)
Theming with CSS Variables
💻css/* globals.css */ :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --primary: 210 40% 98%; --primary-foreground: 222.2 47.4% 11.2%; }
Change it once — it applies across all components.
Most commonly used components
- Form + React Hook Form: Validation, error states, accessible labels
- Data Table + TanStack Table: Sorting, filtering, pagination
- Command: Command palette in the style of Spotlight/VS Code
- Sheet/Dialog: Responsive modals and side panels
- Toast/Sonner: Notifications with queuing
Conclusion
shadcn/ui is the UI foundation I use for every Next.js project. Free, accessible, good-looking out of the box, and most importantly — it never goes stale because you own the code. Combined with Tailwind CSS v4 and Cursor IDE, this is the fastest-to-ship UI stack I know.