| Situação | Use | Porque |
|---|---|---|
| Poucas opções fixas, sem busca | Select | simples, sem overhead de filtro |
| Muitas opções ou precisa buscar | Combobox | tem filtro/itemToStringValue |
| SSR puro sem JS extra | NativeSelect | <select> nativo |
| Seleção múltipla com tags | Combobox multiple+ComboboxChips | Select não suporta multi |
| Notificação transitória | Toast (toast.add) | não bloqueia UI |
| Ação reversível c/ desfazer | Toast + actionProps (Undo) | evita AlertDialog p/ ação leve |
| Confirmação destrutiva/irreversível | Alert Dialog | não fecha ao clicar fora, força decisão |
| Modal comum (form, detalhes) | Dialog | fecha ao clicar fora |
| Painel lateral (filtros, carrinho) | Sheet | side configurável |
| Modal mobile-first | Drawer | swipe nativo, snapPoints |
| Modal que troca por tela | Dialog(desktop)+Drawer(mobile) via useMediaQuery | ver patterns.md |
| Tabela simples sem interação | Table | só marcação |
| Tabela c/ sort/filter/paginação | Data Table (TanStack Table v9 + Table) | Table sozinha não tem esses recursos |
| Gráfico | Chart (Recharts) | já integra --chart-1..5 |
| Menu por clique | Dropdown Menu | trigger explícito |
| Menu por botão direito | Context Menu | mesma API, onContextMenu |
| Barra de menu desktop-style | Menubar | múltiplos MenubarMenu |
| Paleta de comandos (Cmd+K) | Command + CommandDialog | usa cmdk, não Base UI |
| Preview rico ao hover | Hover Card | delay/closeDelay |
| Popup rico ao clique | Popover | clique/foco, não hover |
| Dica curta ao hover | Tooltip | precisa TooltipProvider na raiz |
| Loading com forma conhecida | Skeleton | forma final já conhecida |
| Loading indeterminado inline | Spinner | animate-spin + data-icon |
| Progresso conhecido (%) | Progress | valor controlado |
| Estado vazio | Empty | ícone/título/descrição/ação padrão |
| Lista estática c/ mídia/ações | Item | Field é só p/ input de form |
| Card clicável inteiro | Choice Card (FieldLabel envolve Field) | ver patterns.md |
| Framework | Alias |
|---|---|
| Next.js, Vite, Astro, Laravel, TanStack Router/Start | @/components/ui/* |
| Remix, React Router | ~/components/ui/* |
| Gatsby | @/components/ui/*, só Tailwind v3 |
Todos: npx shadcn@latest init (ou create --template <fw>). Vite e Gatsby exigem configurar o alias tanto no tsconfig quanto no arquivo de build. Projeto existente: Tailwind + cn helper manual, depois shadcn init puro.
| Lib no projeto | Use | Erro |
|---|---|---|
| React Hook Form | Controller+zodResolver | fieldState.invalid pronto |
| TanStack Form | form.Field render-prop | field.state.meta.isTouched && !isValid (manual) |
| Formisch (Valibot) | <Field of={form} path={[...]}> + funções top-level | field.errors (array, mapear p/ {message}) |
| Nenhuma (Next.js simples) | useActionState+Server Action+Zod safeParse | roundtrip ao servidor |
Sempre: data-invalid no Field pai + aria-invalid no controle, nos dois lugares.
registry.json na raiz (name/homepage/items, ou include se dividir por domínio)registry-item.json com type certo: registry:ui/hook/lib/block, registry:page/file (exige target), registry:base (design system), registry:font, registry:item (universal)files[].target com @components/, @ui/, @lib/, @hooks/ em vez de path hardcodedregistryDependencies pra reaproveitar itens (nome simples, @namespace/item, owner/repo/item#ref, URL/path local)shadcn build gera o JSON estático publicadoAuthorization via components.json#registries.@nome.headers, 401/403 customizado (Open in v0 só aceita ?token= em query)Accept/User-Agent, Vary corretonpx shadcn add @nome/item; opcional registrar em registries.json pra @namespace curto global<a> que navega usa buttonVariants(); Button real força role="button" (só ação, não navegação)--rtl quando souber de antemão; migrar depois exige shadcn migrate rtl + ajuste manual de Calendar/Pagination/Sidebar