
Estrutura de Pastas no Frontend Que Escala
Todo projeto começa limpo. Seis meses depois, components/ tem 80 arquivos, ninguém sabe onde fica nada, e o onboarding leva uma semana.
A estrutura de pastas não é o problema. A ausência de regras é.
As Duas Abordagens Comuns
Por tipo (comum no início)
components/
Button.tsx
Modal.tsx
ProductCard.tsx
UserProfile.tsx
hooks/
useCart.ts
useAuth.ts
useProducts.ts
utils/
formatDate.ts
formatPrice.ts
Parece organizado. Mas conforme o projeto cresce, components/ vira um depósito. Nada é agrupado pelo que pertence junto.
Por funcionalidade (o que realmente escala)
features/
cart/
components/
hooks/
utils/
index.ts
auth/
components/
hooks/
utils/
index.ts
products/
components/
hooks/
utils/
index.ts
Cada funcionalidade é dona do seu código. Você consegue ler, alterar ou deletar uma feature sem precisar vasculhar o repositório inteiro.
Uma Estrutura Prática para Next.js App Router
app/
[locale]/
(marketing)/
page.tsx
(app)/
dashboard/
page.tsx
settings/
page.tsx
layout.tsx
components/
ui/ # genéricos, reutilizáveis (Button, Input, Modal)
layout/ # estruturais (Header, Footer, Sidebar)
features/
auth/
components/
LoginForm.tsx
AuthGuard.tsx
hooks/
useAuth.ts
actions/
login.ts
index.ts
cart/
components/
CartDrawer.tsx
CartItem.tsx
hooks/
useCart.ts
store/
cart.store.ts
index.ts
lib/
db.ts
flags.ts
analytics.ts
hooks/ # apenas hooks verdadeiramente globais
utils/ # apenas utilitários verdadeiramente globais
types/ # tipos TypeScript compartilhados
O Princípio da Colocalização
Mantenha o código perto de onde ele é usado.
Se um hook só é usado dentro de CartDrawer.tsx, ele não pertence a /hooks. Ele pertence ao lado do componente.
features/cart/
components/
CartDrawer.tsx
CartDrawer.hooks.ts # usado só aqui
hooks/
useCart.ts # usado em toda a feature de cart
Suba na árvore de pastas apenas quando algo é compartilhado entre features. Não mova arquivos "só por precaução".
O Padrão de Barrel com index.ts
Use index.ts para definir o que uma feature expõe publicamente.
// features/cart/index.ts
export { CartDrawer } from './components/CartDrawer'
export { useCart } from './hooks/useCart'
export type { CartItem } from './types'
Depois importe de forma limpa de outras features:
import { CartDrawer, useCart } from '@/features/cart'
Essa é a API pública da sua feature. Arquivos internos ficam internos.
Quando Barrel Files Dão Errado
Barrel files melhoram os imports — mas têm um custo.
// ❌ Todo arquivo do app é incluído no bundle quando isso é importado
export * from './Button'
export * from './Modal'
export * from './ProductCard'
// ...50 exports a mais
O tree-shaking falha com barrels grandes — e o seu bundle paga o preço.
Regra: Use barrel files no nível de feature (não em components/ui/). Mantenha barrels de UI pequenos e explícitos.
Route Groups no Next.js App Router
Use route groups (nome-do-grupo) para separar responsabilidades sem afetar a URL.
app/
[locale]/
(marketing)/
page.tsx → /pt
about/
page.tsx → /pt/about
(app)/
layout.tsx → layout autenticado compartilhado
dashboard/
page.tsx → /pt/dashboard
settings/
page.tsx → /pt/settings
Páginas de marketing e páginas do app compartilham o parâmetro de locale mas têm layouts diferentes. Route groups os separam de forma limpa.
Erros Comuns
1. Uma pasta components/ gigante
components/
LoginButton.tsx
ProductCard.tsx
CartItem.tsx
DashboardHeader.tsx
AdminTable.tsx
... 70 arquivos a mais
Sem agrupamento = sem estrutura. Extraia para features.
2. hooks/ global para hooks específicos de uma feature
// ❌ hooks/useCartDiscount.ts — usado só no cart
// ✅ features/cart/hooks/useCartDiscount.ts
3. Aninhamento profundo demais
features/
checkout/
components/
steps/
payment/
forms/
fields/
CreditCardField.tsx
Além de 4 níveis, a navegação fica dolorosa. Achate quando o aninhamento não adiciona significado.
4. Importar diretamente entre features
// ❌ Acopla features diretamente
import { CartItem } from '../cart/components/CartItem'
// ✅ Importe pela API pública
import { CartItem } from '@/features/cart'
Isso torna seguro refatorar uma feature sem quebrar outras.
Quando Refatorar a Estrutura
| Sinal | Ação |
|---|---|
| "Onde esse arquivo fica?" — todo mundo pergunta | Clarifique as regras, documente |
| Arquivos de uma feature espalhados em 3+ pastas | Agrupe por feature |
| Deletar uma feature exige busca no repositório inteiro | Reorganize por feature |
| Novos devs levam >1 hora para encontrar coisas | Muito aninhamento ou sem convenções |
O Que Fazer Agora
- Audite sua estrutura atual: conte arquivos por pasta, identifique os depósitos
- Escolha uma feature e mova-a para
features/sua-feature/ - Adicione um
index.tspara definir a API pública dela - Escreva as regras — até um README de 5 linhas em
/featuresajuda no onboarding
Estrutura não é sobre perfeição. É sobre tornar a próxima decisão óbvia.
Quando um novo arquivo chega, a pasta certa deve ser clara sem precisar de uma discussão em equipe. Esse é o objetivo.