--- id: wiki-2026-0508-shadcn-ui title: shadcn/ui category: 10_Wiki/Topics status: verified canonical_id: self aliases: [shadcn, shadcn-ui, shadcn/ui, ui.shadcn.com] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [react, ui, components, tailwind, radix, design-system] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: React 19/Next.js 15/Tailwind 4/Radix UI --- # shadcn/ui ## 매 한 줄 > **"매 install package 가 아니라 매 copy-paste component collection — 매 source code 매 own, 매 customize 자유"**. shadcn (Shaad Heuvel) 매 2023 출시, 매 React + Tailwind + Radix UI 위에 매 well-designed accessible primitive 를 매 CLI 로 가져다 쓰는 anti-library pattern. 2026 현재 매 React app 의 default UI starting kit — Vercel templates, AI SDK examples, Next.js docs 매 default. ## 매 핵심 ### 매 differentiator - **Not a dependency**: 매 `npm install shadcn/ui` 가 아님. 매 `npx shadcn add button` → component code 매 your repo 에 copy. - **Owned source**: 매 매 component file 매 직접 modify. 매 lock-in 없음. - **Built on Radix UI**: accessibility primitives (focus management, ARIA, keyboard) 매 무료. - **Tailwind**: utility-first styling, theme via CSS variables. - **Composable**: 매 small primitive (Button, Input, Dialog) 매 large block 으로 조합. ### 매 stack - **Foundation**: Radix UI Primitives (unstyled accessible components). - **Styling**: Tailwind CSS v4 (2024+ JIT engine). - **Variants**: `class-variance-authority` (cva) — type-safe variant API. - **Class merge**: `tailwind-merge` + `clsx` (`cn()` helper). - **Forms**: `react-hook-form` + `zod` (resolver). - **Charts**: Recharts wrapper (shadcn/charts). - **Icons**: lucide-react. ### 매 CLI workflow ```bash npx shadcn@latest init # one-time setup, creates components.json npx shadcn@latest add button input dialog npx shadcn@latest add https://ui.shadcn.com/r/styles/new-york/login-01.json ``` ### 매 응용 1. SaaS dashboard / admin panel. 2. AI chat UI (shadcn/ui + Vercel AI SDK). 3. Marketing site sections. 4. Internal tools (Retool 대체). 5. Design system foundation (매 fork 후 brand 적용). ## 💻 패턴 ### 1. Initial setup (Next.js 15) ```bash pnpm create next-app@latest my-app --ts --tailwind --app cd my-app pnpm dlx shadcn@latest init # choose: New York style, Slate base, CSS variables pnpm dlx shadcn@latest add button card input form dialog dropdown-menu ``` ### 2. cn() utility (필수 helper) ```typescript // lib/utils.ts import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` ### 3. Button with cva variants ```tsx import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50", { 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 hover:text-accent-foreground", ghost: "hover:bg-accent hover:text-accent-foreground", }, 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" }, } ); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; } export const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button"; return ; } ); Button.displayName = "Button"; ``` ### 4. Form with react-hook-form + zod ```tsx "use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; const schema = z.object({ email: z.string().email(), password: z.string().min(8), }); export function LoginForm() { const form = useForm>({ resolver: zodResolver(schema), defaultValues: { email: "", password: "" }, }); function onSubmit(values: z.infer) { // server action } return (
( Email )} /> ( Password )} /> ); } ``` ### 5. Theming (CSS variables) ```css /* app/globals.css */ @import "tailwindcss"; @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --primary: 221.2 83.2% 53.3%; --primary-foreground: 210 40% 98%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --primary: 217.2 91.2% 59.8%; --primary-foreground: 222.2 47.4% 11.2%; } } ``` ### 6. Data table (TanStack Table integration) ```tsx import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; export function DataTable({ columns, data }: { columns: ColumnDef[]; data: T[] }) { const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() }); return ( {table.getHeaderGroups().map((hg) => ( {hg.headers.map((h) => ( {flexRender(h.column.columnDef.header, h.getContext())} ))} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((c) => ( {flexRender(c.column.columnDef.cell, c.getContext())} ))} ))}
); } ``` ### 7. AI chat UI (Vercel AI SDK + shadcn) ```tsx "use client"; import { useChat } from "@ai-sdk/react"; import { Card } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; export function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat(); return (
{messages.map((m) => ( {m.role}

{m.content}

))}
); } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 빠른 SaaS prototype | shadcn/ui + Next.js + Vercel — 매 default 선택 | | 강한 brand identity | shadcn/ui fork → CSS variables 로 brand 적용 | | 매 component lock-in 싫음 | shadcn/ui (own source) | | 매 dependency 관리 single 파일 선호 | Material UI / Mantine (전통 lib) | | 매 enterprise tabular admin | shadcn/ui + TanStack Table + Recharts | | 매 native mobile (RN) | NativeBase / Tamagui / shadcn-react-native (community) | **기본값**: Next.js 15 + shadcn/ui (New York style) + Tailwind 4 + lucide-react + react-hook-form + zod. ## 🔗 Graph - 부모: [[React]] · [[Design Systems]] - 변형: [[Radix UI]] - 응용: [[Next.js]] - Adjacent: [[CSS_Architecture_and_Styling|Tailwind CSS]] ## 🤖 LLM 활용 **언제**: 매 modern React app 매 빠르게 launch — 매 LLM (Claude/GPT) 매 shadcn 패턴 매 well-trained, 매 generation quality 매 높음. **언제 X**: 매 design-from-scratch creative work — 매 too opinionated. 매 매 non-React framework (Vue/Svelte) — 매 community port 있지만 매 first-class 아님. ## ❌ 안티패턴 - **shadcn 을 npm package 처럼 취급**: 매 update CLI 매 `npx shadcn diff` 로 검토 후 매 manually merge. - **cn() 무시**: 매 className concat 매 직접 — 매 conflict 매 발생. - **Tailwind v3 와 mix**: 매 v4 와 v3 매 incompatible API. 매 lock 명시. - **Radix unstyle 무시 직접 div**: 매 accessibility 손실. - **모든 Button 을 custom variant 로**: 매 base variant 4-5개 유지, 매 outlier 만 inline className. ## 🧪 검증 / 중복 - Verified (ui.shadcn.com docs, Next.js learn, Vercel templates 2025-2026). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — full setup + cva + form + AI chat patterns (2026 stack) |