shadcn-ui

SKILL.md

shadcn/ui — Guia Profissional Completo

shadcn/ui é um conjunto de componentes acessíveis e uma plataforma de distribuição de código. Não é uma biblioteca npm — você copia o código para seu projeto e possui controle total.

Referências Detalhadas

Quando Usar

  • Instalar/configurar shadcn/ui em projeto novo ou existente
  • Adicionar componentes individuais (button, dialog, form, table, etc.)
  • Construir formulários com validação (React Hook Form ou TanStack Form + Zod)
  • Personalizar temas com CSS variables (OKLCH)
  • Implementar dark mode
  • Configurar Data Tables com TanStack Table
  • Montar layouts com Sidebar
  • Criar gráficos com Recharts
  • Configurar Registry custom ou MCP Server
  • Suporte RTL (árabe, hebraico, persa)

Princípios Fundamentais

Princípio Descrição
Open Code Código é copiado para seu projeto — você é dono
Composição Interface composable e previsível via data-slot
Distribuição Schema flat-file + CLI para distribuir componentes
Beautiful Defaults Estilos padrão cuidadosamente escolhidos
AI-Ready Código aberto para LLMs lerem e melhorarem

Quick Start

Novo Projeto (Recomendado)

# Scaffold completo com tema, componentes e presets
npx shadcn@latest create

# OU inicializar em projeto Next.js existente
npx shadcn@latest init

Adicionar Componentes

# Componente individual
npx shadcn@latest add button

# Múltiplos
npx shadcn@latest add button input dialog card select

# Todos
npx shadcn@latest add --all

Uso Básico

import { Button } from "@/components/ui/button"

export default function Page() {
  return <Button variant="outline">Click me</Button>
}

Instalação por Framework

Framework Comando
Next.js npx shadcn@latest init
Vite npx shadcn@latest init
Astro pnpm dlx shadcn@latest init (requer --template with-tailwindcss --add react)
TanStack Start npx shadcn@latest init
React Router npx shadcn@latest init
Laravel npx shadcn@latest init
Manual Ver docs de instalação manual

components.json — Configuração do Projeto

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "ui": "@/components/ui",
    "utils": "@/lib/utils",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "registries": {
    "@v0": "https://v0.dev/chat/b/{name}",
    "@acme": "https://registry.acme.com/{name}.json"
  }
}

Campos importantes:

  • style: "new-york" (padrão; "default" está deprecado)
  • rsc: true adiciona "use client" automaticamente
  • tailwind.cssVariables: true = CSS variables (recomendado) / false = utility classes
  • tailwind.baseColor: "neutral" | "gray" | "zinc" | "stone" | "slate"
  • registries: Suporta múltiplos registries com autenticação

Sistema de Temas (OKLCH)

CSS Variables Padrão

:root {
  --radius: 0.625rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
  --secondary: oklch(0.97 0 0);
  --secondary-foreground: oklch(0.205 0 0);
  --muted: oklch(0.97 0 0);
  --muted-foreground: oklch(0.556 0 0);
  --accent: oklch(0.97 0 0);
  --accent-foreground: oklch(0.205 0 0);
  --destructive: oklch(0.577 0.245 27.325);
  --destructive-foreground: oklch(0.577 0.245 27.325);
  --border: oklch(0.922 0 0);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.708 0 0);
  --chart-1: oklch(0.646 0.222 41.116);
  --chart-2: oklch(0.6 0.118 184.704);
  --chart-3: oklch(0.398 0.07 227.392);
  --chart-4: oklch(0.828 0.189 84.429);
  --chart-5: oklch(0.769 0.188 70.08);
  --sidebar: oklch(0.985 0 0);
  --sidebar-foreground: oklch(0.145 0 0);
  --sidebar-primary: oklch(0.205 0 0);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent: oklch(0.97 0 0);
  --sidebar-accent-foreground: oklch(0.205 0 0);
  --sidebar-border: oklch(0.922 0 0);
  --sidebar-ring: oklch(0.708 0 0);
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  /* ... valores invertidos */
}

Convenção de Nomes

  • --backgroundbg-background (fundo do componente)
  • --foregroundtext-foreground (texto do componente)
  • --primary / --primary-foreground → par de cores para elementos primários
  • Padrão: bg-{nome} para fundo, text-{nome}-foreground para texto

Adicionar Cores Custom

:root {
  --warning: oklch(0.84 0.16 84);
  --warning-foreground: oklch(0.28 0.07 46);
}
.dark {
  --warning: oklch(0.41 0.11 46);
  --warning-foreground: oklch(0.99 0.02 95);
}

@theme inline {
  --color-warning: var(--warning);
  --color-warning-foreground: var(--warning-foreground);
}

Uso: className="bg-warning text-warning-foreground"

Componentes — Visão Geral

Catálogo Completo (60+ componentes)

Formulários & Input: Button, Button Group, Input, Input Group, Input OTP, Textarea, Checkbox, Radio Group, Select, Native Select, Combobox, Switch, Slider, Calendar, Date Picker, Field, Label, Spinner

Layout & Navegação: Accordion, Breadcrumb, Navigation Menu, Sidebar, Tabs, Separator, Scroll Area, Resizable, Pagination, Kbd

Overlays & Dialogs: Dialog, Alert Dialog, Sheet, Drawer, Popover, Tooltip, Hover Card, Context Menu, Dropdown Menu, Menubar, Command

Feedback & Status: Alert, Sonner (Toast), Progress, Spinner, Skeleton, Badge, Empty

Display & Mídia: Avatar, Card, Table, Data Table, Chart, Carousel, Aspect Ratio, Typography, Item, Toggle, Toggle Group, Collapsible, Direction

Padrão de Uso Universal

// 1. Instalar
// npx shadcn@latest add <componente>

// 2. Importar
import { Component } from "@/components/ui/component"

// 3. Usar
<Component variant="..." size="...">Conteúdo</Component>

Formulários — Padrão Moderno (Field + Controller)

React Hook Form + Zod

"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { Controller } from "react-hook-form"
import * as z from "zod"
import { Button } from "@/components/ui/button"
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

const schema = z.object({
  title: z.string().min(5, "Mínimo 5 caracteres").max(32),
  description: z.string().min(20).max(100),
})

export function BugReportForm() {
  const form = useForm<z.infer<typeof schema>>({
    resolver: zodResolver(schema),
    defaultValues: { title: "", description: "" },
  })

  return (
    <form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
      <Controller
        name="title"
        control={form.control}
        render={({ field, fieldState }) => (
          <Field data-invalid={fieldState.invalid}>
            <FieldLabel htmlFor={field.name}>Título</FieldLabel>
            <Input {...field} id={field.name} aria-invalid={fieldState.invalid} />
            <FieldDescription>Título conciso do bug.</FieldDescription>
            {fieldState.invalid && <FieldError errors={[fieldState.error]} />}
          </Field>
        )}
      />
      <Button type="submit">Enviar</Button>
    </form>
  )
}

TanStack Form + Zod

"use client"
import { useForm } from "@tanstack/react-form"
import * as z from "zod"
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

const schema = z.object({
  title: z.string().min(5).max(32),
})

export function MyForm() {
  const form = useForm({
    defaultValues: { title: "" },
    validators: { onSubmit: schema },
    onSubmit: ({ value }) => console.log(value),
  })

  return (
    <form onSubmit={(e) => { e.preventDefault(); form.handleSubmit() }}>
      <form.Field
        name="title"
        children={(field) => {
          const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid
          return (
            <Field data-invalid={isInvalid}>
              <FieldLabel htmlFor={field.name}>Título</FieldLabel>
              <Input
                id={field.name}
                value={field.state.value}
                onBlur={field.handleBlur}
                onChange={(e) => field.handleChange(e.target.value)}
                aria-invalid={isInvalid}
              />
              {isInvalid && <FieldError errors={field.state.meta.errors} />}
            </Field>
          )
        }}
      />
    </form>
  )
}

CLI 3.0 — Comandos Essenciais

Comando Descrição
npx shadcn@latest init Inicializar projeto
npx shadcn@latest add [comp] Adicionar componente(s)
npx shadcn@latest add --all Adicionar todos os componentes
npx shadcn@latest add @acme/comp Instalar de registry custom
npx shadcn@latest view [item] Ver código antes de instalar
npx shadcn@latest search @registry Buscar em registries
npx shadcn@latest build Gerar registry JSON
npx shadcn@latest migrate rtl [path] Migrar para RTL
npx shadcn@latest migrate radix [path] Migrar imports para radix-ui unificado
npx shadcn@latest create Scaffold completo com tema

Flags importantes do add:

  • -y — Skip confirmação
  • -o, --overwrite — Sobrescrever arquivos existentes
  • -a, --all — Todos os componentes
  • -p, --path <path> — Caminho customizado

Dark Mode

Next.js (next-themes)

npm install next-themes
// app/providers.tsx
"use client"
import { ThemeProvider } from "next-themes"

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
      {children}
    </ThemeProvider>
  )
}

// app/layout.tsx
import { Providers } from "./providers"
export default function RootLayout({ children }) {
  return (
    <html lang="pt-BR" suppressHydrationWarning>
      <body><Providers>{children}</Providers></body>
    </html>
  )
}

Toggle de Tema

"use client"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
import { MoonIcon, SunIcon } from "lucide-react"

export function ThemeToggle() {
  const { setTheme, theme } = useTheme()
  return (
    <Button variant="ghost" size="icon" onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
      <SunIcon className="h-4 w-4 dark:hidden" />
      <MoonIcon className="hidden h-4 w-4 dark:block" />
    </Button>
  )
}

MCP Server — Integração com IA

// .mcp.json (Claude Code)
{
  "mcpServers": {
    "shadcn": {
      "command": "npx",
      "args": ["shadcn@latest", "mcp"]
    }
  }
}
// .cursor/mcp.json (Cursor)
{
  "mcpServers": {
    "shadcn": {
      "command": "npx",
      "args": ["shadcn@latest", "mcp"]
    }
  }
}

Permite buscar, visualizar e instalar componentes via linguagem natural.

Tailwind v4 — Mudanças Críticas

Antes (v3) Depois (v4)
@layer base { :root { --bg: 0 0% 100%; } } :root { --bg: oklch(1 0 0); }
@theme { --color-bg: hsl(var(--bg)); } @theme inline { --color-bg: var(--bg); }
hsl(var(--chart-1)) var(--chart-1)
React.forwardRef<> React.ComponentProps<> + data-slot
w-4 h-4 size-4
tailwindcss-animate tw-animate-css
HSL colors OKLCH colors
Estilo default Estilo new-york (padrão)

RTL — Suporte Bidirecional

# Migrar componentes existentes
npx shadcn@latest migrate rtl "src/components/ui/**"
  • CLI transforma left-*/right-*start-*/end-*
  • Animações: slide-in-from-rightslide-in-from-end
  • Adicione dir="rtl" em portais (Popover, Tooltip, etc.)

Constraints e Avisos

  • Não é pacote npm — Componentes copiados ao projeto, você é dono do código
  • "use client" obrigatório na maioria dos componentes interativos
  • Tailwind CSS é dependência obrigatória
  • TypeScript recomendado (suporte JS disponível)
  • Path aliases — Configure @/* no tsconfig.json
  • Estilo "default" deprecado — Use "new-york"
  • Toast deprecado — Use Sonner (npx shadcn@latest add sonner)
  • Radix UI ou Base UI — Ambos suportados desde Dez 2025

Best Practices

  1. Prefira Server Components — Use "use client" só quando necessário
  2. Composição sobre customização — Use o padrão composable dos componentes
  3. CSS Variables — Prefira cssVariables: true para temas dinâmicos
  4. Acessibilidade — Mantenha aria-* e data-slot dos componentes
  5. Validação com Zod — Use Standard Schema (Zod v3+) para forms
  6. Sonner > Toast — Use Sonner para notificações
  7. data-slot — Cada primitivo tem atributo para estilização targeted
  8. Field component — Use Field para forms complexos (compatível com todas form libs)
Weekly Installs
2
First Seen
Feb 26, 2026
Installed on
mcpjam2
gemini-cli2
claude-code2
junie2
windsurf2
zencoder2