Next.js Production Stack
Padrões opinativos para construir e manter aplicações Next.js. Ao criar um novo projeto ou adicionar a um existente, siga estas convenções, a menos que o código existente do usuário claramente faça algo diferente — nesse caso, siga os padrões existentes em vez deste documento e anote brevemente o desvio.
0. Stack resumida
| Camada | Recomendado (preferido, não obrigatório) | Opções a oferecer se uma mudança for solicitada |
|---|---|---|
| Framework | Next.js (App Router), TypeScript, Server Components + Server Actions | nenhuma necessária, este é o núcleo |
| Gerenciador de pacotes | pnpm | npm, yarn, bun |
| UI | Tailwind CSS, shadcn/ui, react-icons, Motion (motion.dev) | Radix UI ou Mantine, lucide-react, CSS Modules |
| Formulários | React Hook Form + Zod | TanStack Form, Valibot em vez de Zod |
| Banco de dados | PostgreSQL + Drizzle ORM | Prisma, Kysely; MySQL ou SQLite para apps pequenos |
| Autenticação | Better Auth, RBAC | Auth.js, Clerk, Supabase Auth |
| Mídia | Cloudinary | UploadThing, S3 + CDN, Vercel Blob |
| Testes | Vitest (unitário/integração), Playwright (e2e) | Jest, Cypress |
| Monitoramento | Sentry, logs estruturados | Datadog, Highlight, Axiom + pino |
| Lint/Formatação | ESLint, Prettier, TypeScript strict | Biome |
| CI/CD | GitHub + GitHub Actions, Vercel, preview deployments | GitLab CI, Netlify, Cloudflare Pages |
Nunca troque silenciosamente um pacote desta tabela por uma alternativa (ex.: Prisma em vez de Drizzle, next-auth em vez de Better Auth). Se o projeto do usuário já usa outra escolha, continue usando-a e sinalize a incompatibilidade em vez de misturar duas soluções para a mesma tarefa.
Estas ferramentas são Recomendadas: o usuário as prefere, mas não são obrigatórias.
- Onde uma seção posterior nomear uma ferramenta específica (pnpm, Drizzle, Better Auth, Cloudinary, Sentry, etc.), leia-a como o padrão recomendado sob esta regra.
- Antes de alterar, substituir ou pular qualquer ferramenta da tabela, pergunte ao usuário primeiro. Ofereça opções: manter a recomendada, ou uma alternativa da última coluna (ou outra prática recomendada), com uma linha de prós e contras para cada e sua própria recomendação.
- Verifique o status atual (manutenção, breaking changes) antes de recomendar uma alternativa. Não confie apenas na memória.
- Nunca mude uma ferramenta sem uma resposta. Até o usuário responder, mantenha o padrão recomendado.
1. Configuração do projeto
- Crie com
pnpm create next-app@latest— TypeScript, App Router, Tailwind, ESLint todos habilitados, recuse o diretóriosrc/para queapp/fique na raiz do projeto (apenas novos projetos). - Projeto existente que já usa
src/: mantenhasrc/—src/app/,src/components/,src/lib/,src/actions/. Useapp/apenas quando o projeto não tiver diretóriosrc/. Siga a estrutura de layout existente e os aliases de importação (@/*→src/*); nunca mova arquivos para fora desrc/como parte de uma tarefa não relacionada. - Use pnpm para todos os comandos de instalação/execução (
pnpm add,pnpm dlx,pnpm dev). Nunca misture lockfiles npm/yarn. - Inicialize shadcn/ui via
pnpm dlx shadcn@latest init, depois adicione componentes sob demanda (pnpm dlx shadcn@latest add button) em vez de pré-instalar o conjunto completo. - Estrutura de pastas base (sem
src/—app/fica na raiz do projeto):
# novos projetos (sem src/)
app/ # rotas, layouts, error/loading/not-found boundaries
components/ # UI compartilhada (ui/ = primitivos shadcn)
lib/ # db, auth, validações, utils, rate-limit, logger
actions/ # server actions agrupadas por domínio
hooks/
types/
drizzle/ # migrações
Projetos com src/ existentes: mesma estrutura sob src/ (src/app/, src/components/, src/lib/, src/actions/) — os nomes das pastas permanecem idênticos, apenas o prefixo muda.
2. TypeScript & qualidade de código
strict: trueno tsconfig, semanysem um comentário justificando, sem@ts-ignoresem uma issue/motivo vinculado.- ESLint (next/core-web-vitals + typescript-eslint) e Prettier configurados e passando antes do código ser considerado pronto; execute-os, não apenas assuma.
- Prefira tipos de retorno explícitos em funções exportadas e Server Actions.
- Mantenha Server Components como padrão; adicione
"use client"apenas onde interatividade, APIs do navegador ou hooks exigirem, e limite o escopo ao nível mais baixo (o mais folha) possível. - Uma responsabilidade por arquivo/módulo; coloque as server actions de uma rota em
actions/ou um arquivo_actions.tsao lado da rota, não inline em arquivos de página enormes. - Convenções de arquitetura: mantenha um lugar para cada tipo de lógica — acesso a dados em
lib/db, mutações emactions/, UI compartilhada emcomponents/, ajudantes transversais emlib/. Siga a nomenclatura existente do projeto (casing de arquivos, nomenclatura de componentes) em vez de introduzir uma nova convenção no meio do caminho. - Revisão de código: trate cada mudança não trivial como tamanho de PR — um diff claro, linter/typecheck/testes passando antes de solicitar revisão, e uma descrição do que mudou e por quê. Ao revisar (humano ou com assistência de IA), verifique verificações de autenticação ausentes, erros não tratados e desvios de convenção, não apenas se o código funciona.
3. Renderização, dados & performance
- Renderização estática vs dinâmica: padrão para estático; opte por dinâmico (
export const dynamic = 'force-dynamic',cookies(),headers()) apenas quando a rota realmente precisar de dados por requisição. - Busca de dados: busque diretamente em Server Components; use Server Actions para mutações, não fetch no cliente para uma rota de API, a menos que o consumidor seja externo.
- Streaming & Suspense: envolva seções lentas/streamáveis em
<Suspense>com um fallback de skeleton real (não um spinner básico) para que o resto da página possa renderizar e transmitir imediatamente — isso é também o queloading.tsxfornece no nível da rota. - Cache e revalidação: use
use cache/unstable_cache(conforme a versão do Next) para leituras caras; invalide precisamente comrevalidatePath/revalidateTagapós mutações em vez de recorrer aforce-dynamicpara "corrigir" obsolescência. - Pré-busca: pré-busque links que os usuários provavelmente seguirão —
<Link>faz isso automaticamente; não desabilite (prefetch={false}) sem um motivo específico. - Carregamento lazy & divisão de código: carregue lazy componentes do cliente que estão abaixo da dobra ou raramente usados com
next/dynamic; evite grandes bundles do cliente de bibliotecas necessárias apenas em uma página. - Otimização de imagens e fontes: sempre use
next/imagepara imagens raster (largura/altura explícita oufill+ contêiner dimensionado) enext/fontpara fontes — nunca raw<img>ou um<link>para Google Fonts. - Core Web Vitals: trate LCP, CLS, INP como uma restrição real — sem conteúdo tardio que cause mudança de layout, sem mídia sem dimensões, sem scripts síncronos bloqueantes.
4. UI
- Tailwind para estilização; componentes shadcn/ui como os primitivos base, personalizados através dos tokens de tema do projeto (variáveis CSS) em vez de sobrescritas inline únicas espalhadas por arquivos.
- react-icons para iconografia (escolha um conjunto de ícones por projeto, ex.:
react-icons/luou/hi2, e mantenha a consistência). - Motion (motion.dev, anteriormente Framer Motion) para animação; mantenha os componentes animados no lado do cliente e pequenos, e respeite
prefers-reduced-motion. - Projete responsivamente por padrão (breakpoints mobile-first do Tailwind); não envie um layout apenas para desktop.
5. Formulários & validação
- Defina um schema Zod por formulário/formato de dados; reutilize o mesmo schema para validação no cliente (via
@hookform/resolvers/zod) e validação no lado do servidor dentro da Server Action — nunca confie apenas na validação do cliente. - React Hook Form (
useForm) gerencia o estado do cliente; ao enviar, chame uma Server Action, exiba erros de campo a partir do valor de retorno estruturado da ação (não exceções lançadas que terminam em um erro genérico). - Valide
process.envcom um schema Zod na inicialização (umlib/env.ts) para que configurações ausentes/mal formatadas falhem rapidamente em vez de no primeiro uso.
6. Banco de dados (PostgreSQL + Drizzle)
- O schema fica em
lib/db/schema.ts(ou dividido por domínio), migrações geradas viadrizzle-kit generatee aplicadas viadrizzle-kit migrate— nunca edite manualmente arquivos de migração gerados. - Use uma conexão em pool (ex.: via pool do driver ou um provedor como Neon/Supabase pooling) em contextos serverless/edge; não abra um novo cliente por requisição sem pooling.
- Otimização de consultas e prevenção de N+1: use consultas relacionais do Drizzle (
db.query.x.findMany({ with: {...} })) ou joins explícitos em vez de fazer loop e consultar por linha; selecione apenas as colunas necessárias em vez de buscas completas de linhas do tiposelect *; pagine/limite qualquer consulta que possa retornar um conjunto de resultados ilimitado. - Indexe colunas usadas em
WHERE/ORDER BY/joins; adicione-os no schema, não como uma reflexão tardia. - Mantenha uma história de backup/restauração documentada para bancos de dados de produção (snapshots do provedor gerenciado no mínimo) — mencione isso se o setup do usuário não tiver nenhum.
7. Autenticação & segurança
- Better Auth para autenticação (sessões, provedores OAuth, email/senha conforme necessário); nunca implemente manualmente hash de senha ou tokens de sessão.
- RBAC: modele papéis/permissões explicitamente (uma coluna
rolemais um pequeno helper de verificação de permissão) e force a autorização na Server Action / camada de acesso a dados, não apenas ocultando a UI — um botão oculto não é controle de acesso. - Limite de taxa em endpoints sensíveis (auth, envio de formulários, qualquer coisa cara) — ex.: via Upstash Redis ou um limitador token-bucket/sliding-window equivalente.
- Defina cabeçalhos de segurança (via
next.config.jsheaders ou middleware):Content-Security-Policy,X-Frame-Options,Referrer-Policy,Permissions-Policy,Strict-Transport-Security. - Cookies:
httpOnly,secure,sameSite: 'lax'(ou'strict'onde viável) em todos os cookies de autenticação/sessão. - Valide toda entrada externa (dados de formulário, parâmetros de consulta, payloads de webhook) com Zod na fronteira antes de tocar na lógica de negócio ou no banco de dados.
- Nunca registre segredos, tokens ou corpos de requisição completos contendo credenciais.
8. SEO
- Use a Metadata API (
generateMetadata/ exportação estática demetadata) por rota — title, description, Open Graph, Twitter card. - Forneça
app/sitemap.tseapp/robots.tsgerados a partir de dados de rota reais, não de uma lista estática desatualizada. - Defina URLs canônicas via metadata
alternates.canonical, especialmente em qualquer rota acessível através de múltiplas variantes de query-string. - Adicione dados estruturados JSON-LD para tipos de conteúdo que se beneficiam (artigos, produtos, breadcrumbs, organização) usando uma tag script com
type="application/ld+json".
9. Acessibilidade
- HTML semântico primeiro (
button,nav,main,header,label) — recorra a ARIA apenas para preencher uma lacuna real que o HTML semântico não pode cobrir. - Tudo acessível e operável apenas pelo teclado; estados de foco visíveis (não remova outlines
:focus-visiblesem substituí-los). - Cada imagem tem
altsignificativo (alt=""vazio apenas para imagens genuinamente decorativas). - Cada entrada de formulário tem um
<label>real associado (não apenas um placeholder). - Verifique o contraste de cores contra WCAG AA no mínimo para texto e elementos interativos.
10. Tratamento de erros & estados de carregamento
Cada segmento de rota que pode falhar ou demorar deve ter:
error.tsx— error boundary no nível do segmento com uma ação de repetição.global-error.tsx— fallback de nível superior para erros queerror.tsxnão pode capturar.not-found.tsx— para 404s, incluindo chamadasnotFound()de buscas de dados.loading.tsx— fallback do Suspense; faça um skeleton real que corresponda ao layout final, não um spinner básico.
11. Testes
- Vitest para testes unitários (funções puras, validação de schema, lógica de utilidades) e testes de componentes (React Testing Library) — coloque como
*.test.ts(x)ou sob__tests__/. - Playwright para fluxos ponta a ponta: autenticação, jornadas críticas do usuário, envios de formulário. Execute contra uma preview deployment ou build local, não contra produção.
- Novas Server Actions e schemas de validação devem vir com pelo menos um teste básico; não deixe lógica crítica de negócio não testada por padrão.
12. Monitoramento & logging
- Sentry para rastreamento de erros tanto no cliente quanto no servidor (arquivo de instrumentação +
sentry.client.config/sentry.server.config), com source maps enviados no deploy. - Logs estruturados (logs JSON com id da requisição, rota, id do usuário quando disponível) em vez de
console.logad hoc. - Monitore Core Web Vitals em produção (Vercel Analytics ou Sentry Performance) e configure monitoramento de uptime na URL implantada.
13. DevOps
- Git com commits convencionais e com escopo; branch
mainprotegida. - CI do GitHub Actions em cada PR: instalar → lint → typecheck → testes unitários → build (adicione e2e para mudanças maiores).
- Implante na Vercel; cada PR recebe uma preview deployment antes do merge.
- Variáveis de ambiente gerenciadas por ambiente na Vercel, validadas na inicialização via o schema de env do Zod da §5.
14. Mídia
- Cloudinary para mídia enviada/dinâmica pelo usuário: upload via params de upload assinados emitidos pelo servidor (nunca exponha o segredo da API no lado do cliente) e sirva através das URLs de transformação do Cloudinary em vez de reimplementar redimensionamento.
15. Convenções de trabalho para Claude
Ao ser solicitado a adicionar uma funcionalidade em um projeto usando esta stack:
- Verifique schemas/ações/componentes existentes antes de criar novos — estenda, não duplique.
- Novos campos de BD → atualize
schema.ts, gere uma migração, não edite SQL manualmente. - Novo formulário → schema Zod → formulário RHF → Server Action com revalidação no lado do servidor e uma verificação de autorização.
- Nova rota → decida estático vs dinâmico deliberadamente; adicione
loading.tsx/error.tsxse ausente; adicione metadados. - Após uma mutação, revalide o caminho/tag específico afetado — não revalide cegamente todo o app.
- Execute lint/typecheck mentalmente contra o modo strict antes de apresentar código; sinalize qualquer
any, entrada não verificada ou verificação de autenticação ausente em vez de enviar silenciosamente. - Antes de introduzir, substituir ou pular qualquer ferramenta da seção 0, pergunte ao usuário com opções (veja a regra "Recomendado, não obrigatório" na seção 0).
16. Arquivo de regras complementar
AGENTS.md— a regra de workflow do projeto (Fluxo Principal, Fluxo de Funcionalidades, aprovação…