Beginner10 min

Understand shadcn/ui's unique approach to component libraries and why AI tools prefer it.

Introduction to shadcn/ui

shadcn/ui is the component library of choice for AI coding tools. Unlike traditional libraries, you own the code—making it perfect for AI-assisted customization.

What is shadcn/ui?

shadcn/ui is not a component library. It's a collection of reusable components that you copy into your project:

Terminal
Traditional Library:
npm install some-ui  node_modules  import Component

shadcn/ui:
npx shadcn add button  components/ui/button.tsx  Your code

Key Differences

AspectTraditional Libraryshadcn/ui
Installationnpm packageCLI copies files
Code locationnode_modulesYour codebase
CustomizationOverride stylesEdit source
Updatesnpm updateManual or regenerate
Bundle sizeFull libraryOnly what you use

Why AI Tools Love shadcn/ui

1. Code Ownership

AI can read and modify your components:

Terminal
// components/ui/button.tsx - Fully accessible
const buttonVariants = cva(
  "inline-flex items-center justify-center...",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground...",
        destructive: "bg-destructive text-destructive-foreground...",
      },
    },
  }
)

AI can add variants, modify styles, or extend functionality.

2. Consistent Patterns

Every component follows the same structure:

Terminal
// All components have:
// - TypeScript types
// - Tailwind styling
// - Accessible by default
// - Variant support via class-variance-authority

export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
  size?: 'default' | 'sm' | 'lg' | 'icon'
}

3. v0 Integration

Vercel's v0 generates shadcn/ui components directly:

Terminal
v0 prompt: "Create a pricing table"
v0 output: Component using shadcn/ui Card, Button, Badge

4. Tailwind CSS Native

No style conflicts—everything is Tailwind:

Terminal
// All styling is Tailwind classes
<Button className="mt-4 w-full">
  Custom styles just work
</Button>

Architecture

Built on Radix UI

shadcn/ui uses Radix primitives for accessibility:

Terminal
User Interface (what you see)
     
shadcn/ui Components (styling + API)
     
Radix UI Primitives (accessibility + behavior)

Radix provides:

  • Keyboard navigation
  • Screen reader support
  • Focus management
  • ARIA attributes

shadcn/ui adds:

  • Tailwind CSS styling
  • Consistent design tokens
  • Component variants

Component Structure

Every component follows this pattern:

Terminal
// components/ui/card.tsx

import * as React from "react"
import { cn } from "@/lib/utils"

const Card = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "rounded-lg border bg-card text-card-foreground shadow-sm",
      className
    )}
    {...props}
  />
))
Card.displayName = "Card"

export { Card }

Key elements:

  • forwardRef for ref forwarding
  • cn() utility for class merging
  • Tailwind classes for styling
  • Accepts additional className

What's Included

Core Components

Layout:

  • Card, Separator, Aspect Ratio
  • Scroll Area, Resizable

Forms:

  • Button, Input, Textarea, Select
  • Checkbox, Radio, Switch, Slider
  • Form (with react-hook-form)

Feedback:

  • Alert, Toast, Progress
  • Skeleton, Spinner

Navigation:

  • Tabs, Navigation Menu, Breadcrumb
  • Sidebar, Pagination

Overlay:

  • Dialog, Drawer, Sheet
  • Popover, Tooltip, Dropdown Menu
  • Alert Dialog, Context Menu

Data Display:

  • Table, Data Table
  • Avatar, Badge, Calendar

The cn() Utility

Central to shadcn/ui:

Terminal
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

What it does:

Terminal
// Merges classes intelligently
cn("px-4 py-2", "px-8") // "px-8 py-2" - later px wins
cn("bg-red-500", condition && "bg-blue-500") // conditional classes
cn(baseStyles, className) // combines base with custom

AI-Generated Patterns

What AI Typically Generates

Terminal
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"

export function LoginForm() {
  return (
    <Card className="w-[350px]">
      <CardHeader>
        <CardTitle>Login</CardTitle>
      </CardHeader>
      <CardContent>
        <form className="space-y-4">
          <div className="space-y-2">
            <Label htmlFor="email">Email</Label>
            <Input id="email" type="email" placeholder="m@example.com" />
          </div>
          <div className="space-y-2">
            <Label htmlFor="password">Password</Label>
            <Input id="password" type="password" />
          </div>
          <Button type="submit" className="w-full">
            Sign In
          </Button>
        </form>
      </CardContent>
    </Card>
  )
}

Prompting for Components

Terminal
"Create a settings page using shadcn/ui with:
- Card container
- Form with Input and Switch components
- Save button with loading state"
Terminal
"Build a data table using shadcn/ui Table with:
- Sortable columns
- Row selection
- Pagination controls"

Recognizing shadcn/ui

Import Patterns

Terminal
// shadcn/ui imports from your project
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Dialog } from "@/components/ui/dialog"

// Not from node_modules like:
// import { Button } from '@some-library/button'

File Structure

Terminal
components/
└── ui/
    ├── button.tsx
    ├── card.tsx
    ├── dialog.tsx
    ├── input.tsx
    └── ...

The @/ Alias

shadcn/ui projects typically use path aliases:

Terminal
// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]
    }
  }
}

Summary

  • shadcn/ui copies components into your project
  • You own the code—full customization control
  • Built on Radix—accessible by default
  • Tailwind CSS—consistent styling approach
  • AI-friendly—code is readable and modifiable
  • v0 compatible—generates shadcn/ui components directly

Next Steps

Learn how to install and set up shadcn/ui in your Next.js project.

Mark this lesson as complete to track your progress