shadcn/ui
UI, 컴포넌트 및 디자인 시스템을 구축하기 위한 프레임워크입니다. 컴포넌트는 CLI를 통해 사용자 프로젝트에 소스 코드로 추가됩니다.
중요: 모든 CLI 명령어는 프로젝트의 패키지 러너(
npx shadcn@latest,pnpm dlx shadcn@latest, 또는bunx --bun shadcn@latest)를 사용하여 실행하세요. 프로젝트의packageManager필드를 기준으로 선택합니다. 아래 예제는npx shadcn@latest를 사용하지만 프로젝트에 맞는 올바른 러너로 대체하세요.
현재 프로젝트 컨텍스트
!`npx shadcn@latest info --json`
위 JSON에는 프로젝트 구성과 설치된 컴포넌트가 포함되어 있습니다. npx shadcn@latest docs <component>를 사용하여 모든 컴포넌트에 대한 문서 및 예제 URL을 얻을 수 있습니다.
원칙
- 기존 컴포넌트를 우선 사용하세요. 사용자 정의 UI를 작성하기 전에
npx shadcn@latest search를 사용하여 레지스트리를 확인하세요. 커뮤니티 레지스트리도 확인하세요. - 조합하세요, 재발명하지 마세요. 설정 페이지 = Tabs + Card + form 컨트롤입니다. 대시보드 = Sidebar + Card + Chart + Table입니다.
- 사용자 정의 스타일보다 내장 변형을 사용하세요.
variant="outline",size="sm"등. - 시맨틱 색상을 사용하세요.
bg-primary,text-muted-foreground— 절대bg-blue-500과 같은 원시 값을 사용하지 마세요.
중요 규칙
이 규칙은 항상 적용됩니다. 각 규칙은 올바른/잘못된 코드 쌍이 있는 파일에 연결됩니다.
스타일링 및 Tailwind → styling.md
className은 레이아웃에만 사용하고, 스타일링에는 사용하지 마세요. 컴포넌트 색상이나 타이포그래피를 절대 재정의하지 마세요.space-x-*또는space-y-*를 사용하지 마세요.flex와gap-*를 사용하세요. 수직 스택의 경우flex flex-col gap-*를 사용하세요.- 너비와 높이가 같을 때는
size-*를 사용하세요.w-10 h-10대신size-10을 사용하세요. truncate단축어를 사용하세요.overflow-hidden text-ellipsis whitespace-nowrap대신 사용하세요.- 수동
dark:색상 재정의를 하지 마세요. 시맨틱 토큰(bg-background,text-muted-foreground)을 사용하세요. - 조건부 클래스에는
cn()을 사용하세요. 수동 템플릿 리터럴 삼항 연산자를 작성하지 마세요. - 오버레이 컴포넌트에서 수동
z-index를 설정하지 마세요. Dialog, Sheet, Popover 등은 자체적으로 스택 순서를 처리합니다.
폼 및 입력 → forms.md
- 폼은
FieldGroup+Field를 사용합니다. 폼 레이아웃에space-y-*또는grid gap-*와 함께 원시div를 절대 사용하지 마세요. InputGroup은InputGroupInput/InputGroupTextarea를 사용합니다.InputGroup내부에 원시Input/Textarea를 절대 사용하지 마세요.- 입력 내부의 버튼은
InputGroup+InputGroupAddon을 사용합니다. - 옵션 세트(2~7개 선택지)는
ToggleGroup을 사용합니다. 수동 활성 상태를 가진Button을 반복하지 마세요. - 관련 체크박스/라디오 그룹화에는
FieldSet+FieldLegend를 사용합니다. 제목이 있는div를 사용하지 마세요. - 필드 유효성 검사는
data-invalid+aria-invalid를 사용합니다.Field에는data-invalid를, 컨트롤에는aria-invalid를 사용합니다. 비활성화된 경우:Field에data-disabled를, 컨트롤에disabled를 사용합니다.
컴포넌트 구조 → composition.md
- 아이템은 항상 해당 그룹 내부에 있어야 합니다.
SelectItem→SelectGroup.DropdownMenuItem→DropdownMenuGroup.CommandItem→CommandGroup. - 사용자 정의 트리거에는
asChild(radix) 또는render(base)를 사용하세요.npx shadcn@latest info에서base필드를 확인하세요. → base-vs-radix.md - Dialog, Sheet, Drawer는 항상 Title이 필요합니다. 접근성을 위해
DialogTitle,SheetTitle,DrawerTitle이 필요합니다. 시각적으로 숨겨야 한다면className="sr-only"를 사용하세요. - 전체 Card 구성을 사용하세요.
CardHeader/CardTitle/CardDescription/CardContent/CardFooter. 모든 것을CardContent에 덤프하지 마세요. - Button에는
isPending/isLoading이 없습니다.Spinner+data-icon+disabled로 구성하세요. TabsTrigger는 반드시TabsList내부에 있어야 합니다.Tabs에 트리거를 직접 렌더링하지 마세요.Avatar는 항상AvatarFallback이 필요합니다. 이미지 로드에 실패할 경우를 대비합니다.
컴포넌트 사용, 사용자 정의 마크업 금지 → composition.md
- 사용자 정의 마크업보다 기존 컴포넌트를 사용하세요. 스타일링된
div를 작성하기 전에 컴포넌트가 존재하는지 확인하세요. - 콜아웃에는
Alert를 사용하세요. 사용자 정의 스타일링된 div를 만들지 마세요. - 빈 상태에는
Empty를 사용하세요. 사용자 정의 빈 상태 마크업을 만들지 마세요. - 토스트는 프로젝트 베이스를 따릅니다. Base UI 프로젝트의 경우
toast컴포넌트의toast를 사용하세요. Radix 및 React Aria 프로젝트의 경우sonner의toast()를 사용하세요. <hr>또는<div className="border-t">대신Separator를 사용하세요.- 로딩 플레이스홀더에는
Skeleton을 사용하세요. 사용자 정의animate-pulsediv를 사용하지 마세요. - 사용자 정의 스타일링된 span 대신
Badge를 사용하세요.
아이콘 → icons.md
Button의 아이콘은data-icon을 사용합니다. 아이콘에data-icon="inline-start"또는data-icon="inline-end"를 사용하세요.- 컴포넌트 내부의 아이콘에 크기 클래스를 사용하지 마세요. 컴포넌트는 CSS를 통해 아이콘 크기를 처리합니다.
size-4또는w-4 h-4를 사용하지 마세요. - 아이콘을 문자열 키가 아닌 객체로 전달하세요. 문자열 조회 대신
icon={CheckIcon}을 사용하세요.
채팅 및 메시징 → chat.md
- 채팅 UI는 채팅 기본 요소를 구성합니다. 대화에는
MessageScroller를, 행에는Message를, 표면에는Bubble을 사용합니다. 직접 만든 버블div나 원시 스크롤 컨테이너를 사용하지 마세요. MessageScroller는 스크롤 동작을 소유합니다. 스트리밍 팔로우, 앵커링, 최신으로 이동(MessageScrollerButton)이 내장되어 있습니다.useStickToBottom/ResizeObserver훅을 작성하지 마세요.- 첨부 파일은
Attachment를 사용하고, 시스템 노트와 구분선은Marker를 사용합니다.Item카드나Separator+ 레이블을 사용하지 마세요.
CLI
- 프리셋 코드를 직접 디코딩하거나 프리셋 URL을 수동으로 생성하지 마세요.
npx shadcn@latest preset decode <code>,preset url <code>, 또는preset open <code>를 사용하세요. 프로젝트 인식 프리셋 감지를 위해서는npx shadcn@latest preset resolve를 사용하세요. - 프리셋 코드를 CLI에 직접 적용하세요. 기존 프로젝트에는
npx shadcn@latest apply <code>를 사용하고, 초기화 시에는npx shadcn@latest init --preset <code>를 사용하세요.
주요 패턴
다음은 올바른 shadcn/ui 코드를 구분하는 가장 일반적인 패턴입니다. 엣지 케이스는 위의 연결된 규칙 파일을 참조하세요.
// 폼 레이아웃: FieldGroup + Field, div + Label이 아닙니다.
<FieldGroup>
<Field>
<FieldLabel htmlFor="email">이메일</FieldLabel>
<Input id="email" />
</Field>
</FieldGroup>
// 유효성 검사: Field에 data-invalid, 컨트롤에 aria-invalid.
<Field data-invalid>
<FieldLabel>이메일</FieldLabel>
<Input aria-invalid />
<FieldDescription>잘못된 이메일입니다.</FieldDescription>
</Field>
// 버튼의 아이콘: data-icon, 크기 클래스 없음.
<Button>
<SearchIcon data-icon="inline-start" />
검색
</Button>
// 간격: gap-*, space-y-*가 아닙니다.
<div className="flex flex-col gap-4"> // 올바름
<div className="space-y-4"> // 틀림
// 동일한 치수: size-*, w-* h-*가 아닙니다.
<Avatar className="size-10"> // 올바름
<Avatar className="w-10 h-10"> // 틀림
// 상태 색상: Badge 변형 또는 시맨틱 토큰, 원시 색상이 아닙니다.
<Badge variant="secondary">+20.1%</Badge> // 올바름
<span className="text-emerald-600">+20.1%</span> // 틀림
컴포넌트 선택
| 필요 사항 | 사용 |
|---|---|
| 버튼/작업 | 적절한 variant와 함께 Button 사용 |
| 폼 입력 | Input, Select, Combobox, Switch, Checkbox, RadioGroup, Textarea, InputOTP, Slider |
| 2~5개 옵션 간 전환 | ToggleGroup + ToggleGroupItem |
| 데이터 표시 | Table, Card, Badge, Avatar |
| 내비게이션 | Sidebar, NavigationMenu, Breadcrumb, Tabs, Pagination |
| 오버레이 | Dialog (모달), Sheet (사이드 패널), Drawer (바텀 시트), AlertDialog (확인) |
| 피드백 | toast (Base UI), sonner (Radix/Aria), Alert, Progress, Skeleton, Spinner |
| 명령 팔레트 | Dialog 내부의 Command |
| 차트 | Chart (Recharts 래퍼) |
| 레이아웃 | Card, Separator, Resizable, ScrollArea, Accordion, Collapsible |
| 빈 상태 | Empty |
| 메뉴 | DropdownMenu, ContextMenu, Menubar |
| 툴팁/정보 | Tooltip, HoverCard, Popover |
| 채팅 / 대화 UI | MessageScroller, Message, Bubble, Attachment, Marker |
주요 필드
주입된 프로젝트 컨텍스트에는 다음 주요 필드가 포함됩니다:
aliases→ 실제 별칭 접두사(예:@/,~/)를 임포트에 사용하고, 하드코딩하지 마세요.isRSC→true인 경우,useState,useEffect, 이벤트 핸들러 또는 브라우저 API를 사용하는 컴포넌트는 파일 상단에"use client"지시어가 필요합니다. 지시어에 대해 조언할 때는 항상 이 필드를 참조하세요.tailwindVersion→"v4"는@theme inline블록을 사용하고,"v3"는tailwind.config.js를 사용합니다.tailwindCssFile→ 사용자 정의 CSS 변수가 정의된 전역 CSS 파일입니다. 항상 이 파일을 편집하고 새 파일을 만들지 마세요.style→ 컴포넌트 시각적 처리 유형(예:nova,vega).base→ 프리미티브 라이브러리(radix또는base). 컴포넌트 API 및 사용 가능한 props에 영향을 줍니다.iconLibrary→ 아이콘 임포트 방식을 결정합니다.lucide의 경우lucide-react를,tabler의 경우@tabler/icons-react를 사용합니다.lucide-react를 가정하지 마세요.resolvedPaths→ 컴포넌트, 유틸리티, 훅 등의 정확한 파일 시스템 대상 경로입니다.framework→ 라우팅 및 파일 규칙(예: Next.js App Router vs Vite SPA).packageManager→ shadcn이 아닌 모든 종속성 설치(예:pnpm add date-fnsvsnpm install date-fns)에 사용합니다.preset→ 현재 프로젝트에 대한 해석된 프리셋 코드 및 값입니다. 프리셋 정보만 필요한 경우npx shadcn@latest preset resolve --json을 사용하세요.
전체 필드 참조는 cli.md — info 명령어를 참조하세요.
컴포넌트 문서, 예제 및 사용법
npx shadcn@latest docs <component>를 실행하여 컴포넌트의 문서, 예제 및 API 참조 URL을 가져옵니다. 이 URL을 가져와 실제 콘텐츠를 확인하세요.
npx shadcn@latest docs button dialog select
컴포넌트를 생성, 수정, 디버깅 또는 사용할 때는 항상 먼저 npx shadcn@latest docs를 실행하고 URL을 가져오세요. 이렇게 하면 추측하지 않고 올바른 API 및 사용 패턴으로 작업할 수 있습니다.
워크플로우
- 프로젝트 컨텍스트 가져오기 — 위에 이미 주입되어 있습니다. 새로 고침이 필요하면
npx shadcn@latest info를 다시 실행하세요. - 설치된 컴포넌트 먼저 확인 —
add를 실행하기 전에 항상 프로젝트 컨텍스트의components목록을 확인하거나resolvedPaths.ui디렉토리를 나열하세요. 추가되지 않은 컴포넌트를 임포트하지 말고, 이미 설치된 컴포넌트를 다시 추가하지 마세요. - 컴포넌트 찾기 —
npx shadcn@latest search. - 문서 및 예제 가져오기 —
npx shadcn@latest docs <component>를 실행하여 URL을 가져온 다음 가져옵니다. 설치하지 않은 레지스트리 항목을 탐색하려면npx shadcn@latest view를 사용하세요. 설치된 컴포넌트의 변경 사항을 미리 보려면npx shadcn@latest add --diff를 사용하세요. - 설치 또는 업데이트 —
npx shadcn@latest add. 기존 컴포넌트를 업데이트할 때는 먼저--dry-run및--diff를 사용하여 변경 사항을 미리 보세요(컴포넌트 업데이트 참조). - 서드파티 컴포넌트의 임포트 수정 — 커뮤니티 레지스트리(예:
@bundui,@magicui)에서 컴포넌트를 추가한 후, 추가된 UI가 아닌 파일에서@/components/ui/...와 같은 하드코딩된 임포트 경로를 확인하세요. 이 경로는 프로젝트의 실제 별칭과 일치하지 않습니다.npx shadcn@latest info를 사용하여 올바른ui별칭(예:@workspace/ui/components)을 얻고 그에 따라 임포트를 다시 작성하세요. CLI는 자체 UI 파일에 대한 임포트를 다시 작성하지만, 서드파티 레지스트리 컴포넌트는 프로젝트와 일치하지 않는 기본 경로를 사용할 수 있습니다. - 추가된 컴포넌트 검토 — 모든 레지스트리에서 컴포넌트 또는 블록을 추가한 후, 항상 추가된 파일을 읽고 올바른지 확인하세요. 누락된 하위 컴포넌트(예:
SelectGroup없는SelectItem), 누락된 임포트, 잘못된 구성, 중요 규칙 위반이 있는지 확인하세요. 또한 아이콘 임포트를 프로젝트 컨텍스트의iconLibrary에 맞게 교체하세요(예: 레지스트리 항목이lucide-react를 사용하지만 프로젝트가hugeicons를 사용하는 경우, 임포트와 아이콘 이름을 그에 맞게 교체). 모든 문제를 해결한 후 다음 단계로 진행하세요. - 레지스트리는 명시적이어야 함 — 사용자가 블록이나 컴포넌트를 추가하도록 요청할 때, 레지스트리를 추측하지 마세요. 레지스트리가 지정되지 않은 경우(예: 사용자가
@shadcn,@tailark,owner/repo등을 지정하지 않고 "로그인 블록 추가"라고 말함), 어떤 레지스트리를 사용할지 물어보세요. 사용자를 대신하여 레지스트리를 기본값으로 사용하지 마세요. - 프리셋 전환 — 먼저 사용자에게 덮어쓰기, 부분 적용, 병합, 또는 건너뛰기 중 선택하도록 요청하세요.
- 현재 프리셋 검사:
npx shadcn@latest preset resolve. 구조화된 값이 필요할 때는--json을 사용하세요. - 들어오는 프리셋 검사:
npx shadcn@latest preset decode <code>. 프리셋 빌더를 공유하거나 열려면preset url <code>또는preset open <code>를 사용하세요. - 덮어쓰기:
npx shadcn@latest apply <code>. 감지된 컴포넌트, 글꼴, CSS 변수를 덮어씁니다. - 부분 적용:
npx shadcn@latest apply <code> --only theme,font. UI 컴포넌트를 재설치하지 않고 선택한 프리셋 부분만 업데이트합니다. 지원되는 값은theme및font이며, 쉼표로 구분된 조합이 허용됩니다.icon은 의도적으로 지원되지 않습니다. 아이콘 변경은 전체 컴포넌트 재설치가 필요할 수 있기 때문입니다. - 병합:
npx shadcn@latest apply <code> --merge. 기존 컴포넌트와 일부 설정을 유지하면서 새 프리셋 구성과 병합합니다. - 건너뛰기: 프리셋을 적용하지 않고 사용자 작업을 계속합니다.
- 현재 프리셋 검사: