Como Integrar ShadCN com Next.js

Configurar uma biblioteca moderna de componentes de UI não deveria parecer como montar móveis sem instruções. Se você está construindo com Next.js 15 e quer componentes bonitos e personalizáveis sem a frustração de caixa-preta das bibliotecas tradicionais, este guia mostra exatamente como integrar Shadcn UI ao seu projeto.
Este artigo cobre o processo completo de configuração do Shadcn UI com Next.js, desde a criação inicial do projeto até a instalação de componentes, configuração do Tailwind CSS e implementação adequada do modo escuro. Você aprenderá como evitar problemas comuns de compatibilidade com React 19 e configurar um sistema de componentes pronto para produção que você realmente possui.
Principais Pontos
- Shadcn UI copia o código-fonte dos componentes diretamente para seu projeto, dando-lhe controle total de propriedade e personalização
- A CLI cuida da instalação e gerenciamento de dependências, tornando a configuração direta mesmo com considerações de compatibilidade do React 19
- A integração com Tailwind CSS é perfeita, com suporte integrado a temas e implementação de modo escuro
- Os componentes são agnósticos ao framework e funcionam tanto com App Router quanto com Pages Router no Next.js
Por que Escolher Shadcn UI para Seu Projeto Next.js
Shadcn UI adota uma abordagem fundamentalmente diferente para bibliotecas de componentes. Em vez de instalar uma dependência que você não pode modificar, Shadcn copia o código-fonte dos componentes diretamente para seu projeto. Isso significa que você obtém:
- Propriedade completa dos seus componentes - modifique qualquer coisa, a qualquer momento
- Design Tailwind-first que se integra perfeitamente com seus estilos existentes
- Zero overhead de runtime - componentes são apenas código em seu projeto
- Type safety integrada desde o início
- Acessibilidade tratada corretamente por padrão através dos primitivos Radix UI
Esta abordagem brilha particularmente com o App Router do Next.js 15, onde componentes de servidor e limites de cliente requerem design cuidadoso de componentes.
Pré-requisitos e Configuração Inicial
Antes de iniciar sua configuração Shadcn UI Next.js, certifique-se de ter:
- Node.js 18.17 ou superior
- Um gerenciador de pacotes (npm, pnpm, yarn, ou bun)
- Familiaridade básica com React e Tailwind CSS
Criando Seu Projeto Next.js 15
Comece criando um novo projeto Next.js com TypeScript e Tailwind CSS:
npx create-next-app@latest my-app --typescript --tailwind --app
Quando solicitado, selecione estas opções:
- ✓ Would you like to use ESLint? Yes
- ✓ Would you like to use
src/
directory? Yes (recomendado) - ✓ Would you like to customize the default import alias? No
Navegue para seu projeto:
cd my-app
Instalando e Configurando Shadcn UI
A CLI do Shadcn cuida da maior parte do trabalho pesado para instalação de componentes. Execute o comando de inicialização:
npx shadcn@latest init
Você verá várias solicitações de configuração:
- Style: Escolha seu estilo preferido (Default/New York)
- Base color: Selecione entre os esquemas de cores disponíveis
- CSS variables: Use Yes para melhor suporte a temas
Lidando com Dependências Peer do React 19
Se você estiver usando npm com React 19, encontrará avisos de dependências peer:
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
A CLI solicitará que você escolha uma estratégia de resolução:
? How would you like to proceed?
❯ Use --force
Use --legacy-peer-deps
Selecione --legacy-peer-deps
para a abordagem mais segura. Usuários de pnpm, yarn ou bun não verão estes avisos e podem prosseguir normalmente.
Adicionando Seus Primeiros Componentes
Com o Shadcn inicializado, adicione componentes usando a CLI:
npx shadcn@latest add button card
Este comando:
- Baixa o código-fonte do componente
- Coloca-o em
src/components/ui/
- Instala quaisquer dependências necessárias (como primitivos Radix UI)
- Atualiza suas importações de componentes
Aqui está como um componente button típico do Shadcn fica após a instalação:
// src/components/ui/button.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 transition-colors focus-visible:outline-none focus-visible:ring-2",
{
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",
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",
},
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<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }
Observe como o componente é totalmente personalizável - você pode modificar variantes, adicionar novas ou alterar o estilo completamente.
Configurando Tailwind CSS para Shadcn
Shadcn UI funciona tanto com Tailwind CSS v3 quanto v4. A CLI configura automaticamente seu tailwind.config.js
durante a inicialização. Aqui está o que é adicionado:
// tailwind.config.js
module.exports = {
darkMode: ["class"],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: [require("tailwindcss-animate")],
}
Atualize Seu globals.css
A CLI também atualiza seu globals.css
com variáveis CSS para temas:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96%;
--secondary-foreground: 222.2 84% 4.9%;
--muted: 210 40% 96%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96%;
--accent-foreground: 222.2 84% 4.9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 84% 4.9%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 94.1%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
Implementando Modo Escuro com next-themes
Para uma implementação de modo escuro pronta para produção, use next-themes para evitar problemas de hidratação.
Instalar next-themes
npm install next-themes
Criar um Theme Provider
Crie src/components/theme-provider.tsx
:
"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>
}
Atualize Seu Layout Raiz
Modifique src/app/layout.tsx
:
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}
</ThemeProvider>
</body>
</html>
)
}
Criar um Componente Theme Toggle
"use client"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
export function ThemeToggle() {
const { setTheme, theme } = useTheme()
return (
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
)
}
Trabalhando com Mudanças do React 19
React 19 introduz várias mudanças que afetam como você trabalha com componentes Shadcn:
Manuseio Simplificado de ref
React 19 permite passar ref
como uma prop regular, eliminando a necessidade de forwardRef
em muitos casos:
// Abordagem antiga (React 18)
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, ...props }, ref) => {
return <input ref={ref} className={className} {...props} />
}
)
// Nova abordagem (React 19)
function Input({ className, ref, ...props }: InputProps & { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} className={className} {...props} />
}
Novos Hooks de Formulário
Os hooks useActionState
e useFormStatus
do React 19 funcionam perfeitamente com componentes de formulário Shadcn:
import { useActionState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
function ContactForm() {
const [state, formAction] = useActionState(async (prevState, formData) => {
// Lógica da server action aqui
const email = formData.get("email")
// Processar formulário...
return { success: true }
}, { success: false })
return (
<form action={formAction}>
<Input name="email" type="email" required />
<Button type="submit">Submit</Button>
{state.success && <p>Obrigado por se inscrever!</p>}
</form>
)
}
Problemas Comuns e Soluções
Erros de Build com Tailwind CSS
Se você encontrar erros de build após a configuração, certifique-se de que sua configuração PostCSS está correta:
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Estilização de Componente Não Aplicada
Verifique se seu globals.css
está importado em seu layout raiz:
import "./globals.css"
Erros TypeScript com Props de Componente
Certifique-se de que seu tsconfig.json
inclui os caminhos corretos:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
Melhores Práticas para Produção
-
Personalize o utilitário cn: A função
cn
emlib/utils.ts
mescla classes inteligentemente. Estenda-a para suas necessidades específicas. -
Crie variantes de componente: Use CVA (class-variance-authority) para criar variantes consistentes de componentes em sua aplicação.
-
Otimize o tamanho do bundle: Instale apenas os componentes que você realmente usa. Cada componente é independente.
-
Teste acessibilidade: Componentes Shadcn usam primitivos Radix UI que lidam com acessibilidade, mas sempre teste com leitores de tela.
-
Controle de versão: Como os componentes são copiados para seu projeto, faça commit deles no controle de versão e rastreie mudanças.
Conclusão
Configurar Shadcn UI com Next.js oferece um sistema de componentes moderno que equilibra flexibilidade com velocidade de desenvolvimento. Ao contrário das bibliotecas de componentes tradicionais, você possui cada linha de código, pode personalizar qualquer coisa e não fica preso às decisões de design de outra pessoa.
A combinação do App Router do Next.js 15, melhorias do React 19 e Tailwind CSS cria uma base poderosa para construir aplicações web modernas. Com a abordagem do Shadcn de copiar componentes em vez de instalá-los como dependências, você obtém o melhor dos dois mundos: desenvolvimento rápido e controle completo.
FAQs
Ao usar npm com React 19, você precisará usar a flag --legacy-peer-deps durante a instalação de componentes devido a conflitos de dependências peer. Gerenciadores de pacotes como pnpm, yarn e bun lidam com essas dependências de forma mais elegante e não requerem flags especiais. O resultado final é o mesmo independentemente do gerenciador de pacotes que você escolher.
Sim, Shadcn UI funciona tanto com App Router quanto com Pages Router. O código do componente em si é agnóstico ao framework. A principal diferença está em como você implementa providers como o theme provider - no Pages Router, você envolveria seu app em _app.tsx em vez do layout raiz.
Como o Shadcn copia componentes diretamente para seu projeto, você pode modificá-los como qualquer outro código. Abra o arquivo do componente em src/components/ui/, ajuste as classes Tailwind ou variantes CVA e salve. Você também pode modificar as variáveis de tema globais em seu CSS para alterar cores e espaçamento em todos os componentes de uma vez.
Não, você deve instalar apenas os componentes conforme precisar deles. Cada componente é independente e inclui suas próprias dependências. Isso mantém o tamanho do seu bundle mínimo e seu projeto organizado. Use npx shadcn@latest add [nome-do-componente] para adicionar componentes individuais quando necessário.
Como os componentes são copiados para seu projeto, eles não serão atualizados automaticamente quando Shadcn lançar novas versões. Você pode verificar manualmente a documentação do Shadcn UI para atualizações e executar novamente o comando add para componentes específicos ou aplicar mudanças manualmente. Isso lhe dá controle sobre quando e como os componentes são atualizados em seu projeto.