Shadcn/UI: Tutorial para Iniciantes e Primeiros Passos

Audrey Lopez

Audrey Lopez

14 junho 2025

Shadcn/UI: Tutorial para Iniciantes e Primeiros Passos

Para desenvolvedores web, a busca pelo kit de ferramentas de UI perfeito é um esforço constante. Por anos, desenvolvedores React confiaram em bibliotecas de componentes tradicionais como Material-UI (MUI), Ant Design e Chakra UI. Essas bibliotecas oferecem uma vasta gama de componentes pré-construídos, prometendo acelerar o desenvolvimento. No entanto, elas frequentemente vêm com uma desvantagem: falta de controle, substituições de estilo que parecem uma batalha e tamanhos de bundle inflados.

Apresentando o Shadcn UI, uma abordagem que muda paradigmas e que conquistou a comunidade React. Não é uma biblioteca de componentes como você está acostumado; é algo melhor. É uma coleção de componentes lindamente projetados, acessíveis e infinitamente reutilizáveis que você não instala do npm como uma dependência — você os copia diretamente para o seu projeto.

Este tutorial abrangente, com 4000 palavras, servirá como seu guia definitivo, levando você de um iniciante completo a um praticante confiante do Shadcn UI. Exploraremos sua filosofia fundamental, detalharemos a configuração, construiremos UIs complexas, dominaremos temas avançados e manipulação de formulários, e discutiremos as melhores práticas para aplicações em larga escala. Prepare-se para repensar o que você espera de um kit de ferramentas de UI.

💡
Quer uma ótima ferramenta de Teste de API que gera Documentação de API linda?

Quer uma plataforma integrada, Tudo-em-Um para sua Equipe de Desenvolvedores trabalhar com máxima produtividade?

Apidog entrega todas as suas demandas, e substitui o Postman por um preço muito mais acessível!
button

A Filosofia do Shadcn UI - Uma Nova Forma de Construir

Antes de escrever uma única linha de código, é fundamental entender por que o Shadcn UI existe e quais problemas ele resolve. Compreender essa filosofia central é a chave para liberar todo o seu potencial.

O Que o Shadcn UI Não É

O Que o Shadcn UI É

A principal vantagem deste modelo é a fusão de velocidade e controle. Você obtém a velocidade inicial de usar componentes pré-construídos sem sacrificar a flexibilidade e a manutenibilidade a longo prazo que vêm de possuir seu próprio código.


Preparando o Terreno - Configuração e Instalação do Projeto

Vamos transitar da teoria para a prática. Configuraremos um novo projeto do zero. Para este guia, usaremos principalmente o Next.js, pois seus componentes de servidor e roteamento baseado em arquivos se alinham perfeitamente com o ethos do Shadcn UI. Também abordaremos brevemente a configuração para Vite.

Passo 1: Pré-requisitos do Ambiente

Certifique-se de que seu ambiente de desenvolvimento está pronto. Você precisará de:

Passo 2: Criando uma Nova Aplicação Next.js

Abra seu terminal e execute o seguinte comando para inicializar um novo projeto Next.js.Bash

npx create-next-app@latest my-pro-shadcn-app --typescript --tailwind --eslint

Este comando estrutura uma nova aplicação em um diretório chamado my-pro-shadcn-app. Incluímos algumas flags importantes:

O instalador fará algumas perguntas. Estas são as escolhas recomendadas para uma configuração moderna do Next.js 14+:

✔ Would you like to use `src/` directory? … No / **Yes**
✔ Would you like to use App Router? (recommended) … No / **Yes**
✔ Would you like to customize the default import alias? … **No** / Yes

Usar o App Router é uma prática padrão, e o diretório src/ ajuda na organização do código. Uma vez feito, navegue para o seu novo projeto:Bash

cd my-pro-shadcn-app

Passo 3: O Comando init - Dando Vida ao Shadcn UI

Este é o passo mais importante. O Shadcn UI fornece uma ferramenta CLI para configurar seu projeto. Execute o seguinte comando a partir do diretório raiz do seu projeto:Bash

npx shadcn-ui@latest init

Isso iniciará um questionário interativo para configurar seu projeto. Vamos detalhar cada pergunta e seu significado:

Depois de confirmar, o CLI faz sua mágica:

  1. Instala Dependências: Adiciona pacotes necessários como tailwindcss-animate e class-variance-authority.
  2. Cria components.json: Armazena suas escolhas de configuração.
  3. Atualiza tailwind.config.ts: Injeta o plugin do Shadcn UI e a configuração de temas.
  4. Atualiza globals.css: Adiciona um grande bloco de variáveis CSS que definem toda a sua paleta de cores, raios de borda e muito mais.
  5. Cria lib/utils.ts: Este arquivo exporta uma função auxiliar cn, que é uma utilidade inteligente para mesclar condicionalmente classes Tailwind CSS.

Seu projeto agora está totalmente configurado.

(Alternativa: Configuração com Vite)

Se você estiver usando Vite com React, o processo é muito semelhante. Após configurar um projeto Vite + React + TS, você instalaria manualmente o Tailwind CSS e então executaria npx shadcn-ui@latest init. O CLI é inteligente o suficiente para detectar uma configuração Vite e fará perguntas ligeiramente diferentes sobre a localização dos arquivos (por exemplo, index.css em vez de globals.css).


Construindo uma UI - De Componentes Simples a Layouts Complexos

Com a configuração completa, vamos começar a construir. O fluxo de trabalho principal é: identifique uma necessidade, adicione o componente, use-o.

Passo 4: Adicionando e Usando Seus Primeiros Componentes

Vamos limpar o boilerplate padrão do Next.js e construir uma interface simples.

1. Adicionar um Botão:Bash

npx shadcn-ui@latest add button

Observe o que acontece: um novo arquivo, src/components/ui/button.tsx, é criado. Este é o seu botão. Você é o dono dele.

2. Adicionar um Card:Bash

npx shadcn-ui@latest add card

Este comando é mais interessante. Ele cria src/components/ui/card.tsx. Se você inspecionar este arquivo, verá que ele exporta múltiplos componentes: Card, CardHeader, CardTitle, CardDescription, CardContent e CardFooter. Este é um padrão comum para componentes compostos.

3. Construir a UI:

Agora, abra src/app/page.tsx e substitua seu conteúdo pelo seguinte:TypeScript

import { Button } from "@/components/ui/button";
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input"; // We'll add this next
import { Label } from "@/components/ui/label";   // And this

export default function Home() {
  return (
    <main className="flex min-h-screen items-center justify-center bg-background p-8">
      <Card className="w-full max-w-md">
        <CardHeader>
          <CardTitle className="text-2xl">Create Project</CardTitle>
          <CardDescription>
            Deploy your new project in one-click.
          </CardDescription>
        </CardHeader>
        <CardContent className="grid gap-4">
          <div className="grid gap-2">
            <Label htmlFor="name">Name</Label>
            <Input id="name" placeholder="Name of your project" />
          </div>
          <div className="grid gap-2">
            <Label htmlFor="framework">Framework</Label>
            {/* We'll replace this with a Select component later */}
            <Input id="framework" placeholder="e.g. Next.js" />
          </div>
        </CardContent>
        <CardFooter>
          <Button className="w-full">Deploy</Button>
        </CardFooter>
      </Card>
    </main>
  );
}

Nosso código ainda não funcionará porque estamos faltando os componentes Input e Label. Vamos adicioná-los:Bash

npx shadcn-ui@latest add input
npx shadcn-ui@latest add label

Agora, execute seu servidor de desenvolvimento:Bash

npm run dev

Navegue para http://localhost:3000. Você verá um formulário limpo e com aparência profissional dentro de um card. Observe como usamos classes utilitárias como w-full, max-w-md e grid diretamente em nosso JSX para controlar o layout. Este é o poder de combinar Shadcn e Tailwind CSS.

Passo 5: Introduzindo Componentes Mais Sofisticados

Entradas estáticas são boas, mas aplicações reais precisam de elementos interativos. Vamos aprimorar nosso formulário.

1. Adicionar um Componente Select: A entrada "Framework" deve ser um dropdown. Vamos adicionar o componente Select. Este é mais complexo e tem dependências de outros componentes.Bash

npx shadcn-ui@latest add select

O CLI é inteligente. Ele verá que o Select requer um componente Popover para funcionar e pedirá sua permissão para instalá-lo e suas dependências também. Esta é uma característica fantástica que impede você de ter que rastrear dependências manualmente.

2. Integrar o Componente Select: Substitua o Input para "Framework" em src/app/page.tsx pelo novo componente Select.TypeScript

// Add these imports at the top
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

// ... inside the CardContent
<div className="grid gap-2">
  <Label htmlFor="framework">Framework</Label>
  <Select>
    <SelectTrigger id="framework">
      <SelectValue placeholder="Select a framework" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="nextjs">Next.js</SelectItem>
      <SelectItem value="sveltekit">SvelteKit</SelectItem>
      <SelectItem value="astro">Astro</SelectItem>
      <SelectItem value="nuxt">Nuxt.js</SelectItem>
    </SelectContent>
  </Select>
</div>

Atualize seu navegador. Agora você tem um dropdown select totalmente funcional e acessível, completo com animações e navegação por teclado adequada, tudo graças ao Radix UI funcionando nos bastidores.

3. Adicionando Feedback ao Usuário com Toast: O que acontece quando um usuário clica em "Deploy"? Devemos dar a ele algum feedback. O componente Toast é perfeito para isso.

Primeiro, adicione-o:Bash

npx shadcn-ui@latest add toast

Em seguida, para usar toasts, você precisa adicionar um componente <Toaster /> ao seu layout raiz para que ele possa ser exibido em qualquer lugar na aplicação. Abra src/app/layout.tsx e modifique-o:TypeScript

import { Toaster } from "@/components/ui/toaster" // Import the Toaster

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster /> {/* Add it here, just before closing body */}
      </body>
    </html>
  )
}

Agora, precisamos de uma maneira de disparar o toast. Usaremos o hook useToast. Vamos atualizar src/app/page.tsx para torná-lo um componente cliente e lidar com o clique do botão.TypeScript

'use client'; // <-- Add this at the very top of the file

// ... other imports
import { useToast } from "@/components/ui/use-toast";

export default function Home() {
  const { toast } = useToast(); // Get the toast function from the hook

  function handleDeploy() {
    toast({
      title: "Deployment Scheduled!",
      description: "Your project 'Name of your project' is being deployed.",
      duration: 5000,
    });
  }

  return (
    <main className="flex min-h-screen items-center justify-center bg-background p-8">
      <Card className="w-full max-w-md">
        {/* ... CardHeader and CardContent ... */}
        <CardFooter>
          <Button className="w-full" onClick={handleDeploy}> {/* Add onClick handler */}
            Deploy
          </Button>
        </CardFooter>
      </Card>
    </main>
  );
}

Agora, quando você clicar no botão "Deploy", uma notificação elegante aparecerá no canto da sua tela.


Construindo um Formulário Profissional com Validação

A maioria das aplicações do mundo real requer um tratamento robusto de formulários, incluindo validação do lado do cliente. A maneira oficial de lidar com isso usando Shadcn UI é combiná-lo com react-hook-form para gerenciamento de estado e zod para validação de esquema. Vamos construir isso.

Passo 6: Instalando Dependências de Formulário

Primeiro, vamos instalar as bibliotecas necessárias:Bash

npm install react-hook-form zod @hookform/resolvers

Passo 7: Adicionando o Componente Form do Shadcn

O Shadcn UI fornece um componente Form especial que atua como um wrapper para conectar perfeitamente o react-hook-form com seus componentes de UI.Bash

npx shadcn-ui@latest add form

Isso adicionará src/components/ui/form.tsx. Este arquivo fornece um conjunto de componentes cientes de contexto (Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage) que reduzem drasticamente o boilerplate.

Passo 8: Criando o Esquema de Validação

Em seu src/app/page.tsx, vamos definir a forma e as regras dos dados do nosso formulário usando zod.TypeScript

// Add these imports at the top
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";

Agora, vamos criar o esquema logo acima do nosso componente Home:TypeScript

const formSchema = z.object({
  projectName: z.string().min(2, {
    message: "Project name must be at least 2 characters.",
  }).max(50, {
    message: "Project name must not exceed 50 characters.",
  }),
  framework: z.string({
    required_error: "Please select a framework to display.",
  }),
});

Este esquema define dois campos: projectName deve ser uma string entre 2 e 50 caracteres, e framework é uma string obrigatória.

Passo 9: Conectando o Formulário

Agora, vamos refatorar nosso componente Home para usar todas essas novas ferramentas.TypeScript

export default function Home() {
  const { toast } = useToast();

  // 1. Define your form.
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      projectName: "",
    },
  });

  // 2. Define a submit handler.
  function onSubmit(values: z.infer<typeof formSchema>) {
    // Do something with the form values.
    // ✅ This will be type-safe and validated.
    console.log(values);
    toast({
      title: "You submitted the following values:",
      description: (
        <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
          <code className="text-white">{JSON.stringify(values, null, 2)}</code>
        </pre>
      ),
    });
  }

  // 3. Build the JSX with Shadcn's Form components
  return (
    <main className="flex min-h-screen items-center justify-center bg-background p-8">
      <Card className="w-full max-w-md">
        <CardHeader>
          <CardTitle className="text-2xl">Create Project</CardTitle>
          <CardDescription>
            Deploy your new project in one-click.
          </CardDescription>
        </CardHeader>
        <CardContent>
          <Form {...form}>
            <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
              <FormField
                control={form.control}
                name="projectName"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Name</FormLabel>
                    <FormControl>
                      <Input placeholder="Name of your project" {...field} />
                    </FormControl>
                    <FormDescription>
                      This is your public display name.
                    </FormDescription>
                    <FormMessage /> {/* Displays validation errors */}
                  </FormItem>
                )}
              />
              <FormField
                control={form.control}
                name="framework"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Framework</FormLabel>
                    <Select onValueChange={field.onChange} defaultValue={field.value}>
                      <FormControl>
                        <SelectTrigger>
                          <SelectValue placeholder="Select a framework" />
                        </SelectTrigger>
                      </FormControl>
                      <SelectContent>
                        <SelectItem value="nextjs">Next.js</SelectItem>
                        <SelectItem value="sveltekit">SvelteKit</SelectItem>
                        <SelectItem value="astro">Astro</SelectItem>
                        <SelectItem value="nuxt">Nuxt.js</SelectItem>
                      </SelectContent>
                    </Select>
                    <FormDescription>
                      The framework you want to deploy.
                    </FormDescription>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <Button type="submit" className="w-full">Deploy</Button>
            </form>
          </Form>
        </CardContent>
      </Card>
    </main>
  );
}

Este é um trecho de código significativo, mas é um padrão incrivelmente poderoso e escalável. O componente FormField lida com todas as conexões de estado, e FormMessage exibe automaticamente o erro de validação correto do seu esquema zod quando um usuário interage com o campo. Tente enviar o formulário com um nome de projeto vazio para ver a validação em ação.


Dominando Temas e Personalização

O verdadeiro poder do Shadcn UI é liberado quando você começa a torná-lo seu.

Passo 10: Temas Avançados com Variáveis CSS

Todo o seu tema é definido por variáveis CSS em src/app/globals.css. Abra este arquivo e procure pelos blocos :root e .dark.CSS

/* Example from globals.css */
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 222.2 47.4% 11.2%;
  --primary-foreground: 210 40% 98%;
  /* ... and many more */
  --radius: 0.5rem;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 210 40% 98%;
  --primary-foreground: 222.2 47.4% 11.2%;
  /* ... */
}

Implementando o Modo Escuro:

O Shadcn é pré-configurado para o modo escuro graças ao bloco de classe .dark e à estratégia darkMode: "class" do Tailwind em tailwind.config.ts. Tudo o que você precisa é uma maneira de alternar a classe dark no elemento <html>. Uma biblioteca popular para isso é next-themes.

  1. Instale-a: npm install next-themes
  2. Crie um componente ThemeProvider (src/components/theme-provider.tsx): TypeScript
"use client"
import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
  1. Envolva seu RootLayout neste provider (src/app/layout.tsx): TypeScript
import { ThemeProvider } from "@/components/theme-provider"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
          <Toaster />
        </ThemeProvider>
      </body>
    </html>
  )
}
  1. Finalmente, crie um botão de alternância (por exemplo, src/components/mode-toggle.tsx): TypeScript
"use client"
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"

export function ModeToggle() {
  const { theme, setTheme } = useTheme()

  return (
    <Button
      variant="outline"
      size="icon"
      onClick={() => setTheme(theme === "light" ? "dark" : "light")}
    >
      <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
      <span className="sr-only">Toggle theme</span>
    </Button>
  )
}

Agora você pode colocar este <ModeToggle /> em qualquer lugar da sua aplicação para obter um alternador de modo escuro ciente do sistema e que pode ser sobrescrito pelo usuário.

Passo 11: Personalizando o Código-Fonte do Componente

Este é o superpoder definitivo. Digamos que você queira uma nova variante de sucesso para o seu botão que tenha um fundo verde.

Abra src/components/ui/button.tsx. Encontre a definição de buttonVariants. Ela usa cva (Class Variance Authority). Simplesmente adicione uma nova variante:TypeScript

const buttonVariants = cva(
  // ... base styles
  {
    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",
        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
        success: "bg-green-600 text-white hover:bg-green-600/90", // Our new variant
      },
      // ... size variants
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

É isso. Agora você pode usá-lo em seu código: <Button variant="success">Success</Button>. Você não precisou escrever substituições de CSS complexas. Você apenas editou o próprio código-fonte do componente. Este fluxo de trabalho é simples, previsível e incrivelmente poderoso.


Parte 6: Melhores Práticas e o Caminho a Seguir

À medida que sua aplicação cresce, aqui estão algumas melhores práticas para ter em mente.

Conclusão: Você É o Autor da Biblioteca

Você agora viajou da filosofia central do Shadcn UI para a implementação de padrões avançados do mundo real. Você viu que sua verdadeira inovação não são apenas os componentes em si, mas a mudança de paradigma que ele representa. Ele move os desenvolvedores de serem meros consumidores de uma biblioteca para serem curadores e proprietários de seu próprio kit de ferramentas de UI.

Ao fornecer o código-fonte bruto, construindo sobre as bases sólidas do Tailwind CSS e Radix UI, e oferecendo uma experiência de CLI fluida, o Shadcn UI atinge o equilíbrio perfeito entre a velocidade de desenvolvimento inicial e a manutenibilidade a longo prazo e a liberdade criativa. Você não está mais limitado pelo sistema de design de outra pessoa. Os componentes em seu projeto são seus — para modificar, estender e aperfeiçoar.

O futuro da UI da sua aplicação não está mais nas mãos de uma dependência de terceiros; está bem ali na sua pasta components. Boa construção.

💡
Quer uma ótima ferramenta de Teste de API que gera Documentação de API linda?

Quer uma plataforma integrada, Tudo-em-Um para sua Equipe de Desenvolvedores trabalhar com máxima produtividade?

Apidog entrega todas as suas demandas, e substitui o Postman por um preço muito mais acessível!
button

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs